@hzab/form-render 1.6.10 → 1.6.12
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,14 +1,22 @@
|
|
1
|
+
# @hzab/form-render@1.6.12
|
2
|
+
|
3
|
+
feat: PersonnelSelect组件优化传值方式,支持复数据传入、返回
|
4
|
+
|
5
|
+
# @hzab/form-render@1.6.11
|
6
|
+
|
7
|
+
feat: editor添加配置merage
|
8
|
+
|
1
9
|
# @hzab/form-render@1.6.10
|
2
10
|
|
3
11
|
feat: 富文本编辑器实例像外暴露
|
4
12
|
|
5
13
|
# @hzab/form-render@1.6.9
|
6
14
|
|
7
|
-
feat: PersonnelSelect组件
|
15
|
+
feat: PersonnelSelect 组件
|
8
16
|
|
9
17
|
# @hzab/form-render@1.6.8
|
10
18
|
|
11
|
-
feat:富文本组件添加私有化部署文件上传与oss上传的区分
|
19
|
+
feat:富文本组件添加私有化部署文件上传与 oss 上传的区分
|
12
20
|
|
13
21
|
# @hzab/form-render@1.6.7
|
14
22
|
|
package/package.json
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import React, { useEffect, useState, useRef, useCallback, useMemo } from "react";
|
2
2
|
import { Select, List, Avatar, Spin } from "antd";
|
3
3
|
import { connect, mapProps } from "@formily/react";
|
4
|
-
import { debounce, isObject } from "lodash";
|
5
|
-
import type {
|
4
|
+
import { debounce, isObject, isPlainObject } from "lodash";
|
5
|
+
import type { LabelValue, ScrollPagination, RemoteSelectProps } from "./type";
|
6
6
|
|
7
7
|
import "./index.less";
|
8
8
|
|
@@ -14,7 +14,7 @@ const defaultListItemConfigs = [
|
|
14
14
|
{ label: "身份证号:", key: "idnumber" },
|
15
15
|
];
|
16
16
|
|
17
|
-
const transfromLabelInValueData = (list:
|
17
|
+
const transfromLabelInValueData = (list: LabelValue[], labelKey: string, valueKey: string): LabelValue[] => {
|
18
18
|
try {
|
19
19
|
return list.map((item) => ({ ...item, label: item?.[labelKey], value: item?.[valueKey] }));
|
20
20
|
} catch (e) {
|
@@ -24,7 +24,8 @@ const transfromLabelInValueData = (list: Person[], labelKey: string, valueKey: s
|
|
24
24
|
};
|
25
25
|
|
26
26
|
const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
27
|
-
|
27
|
+
loadData,
|
28
|
+
searchData,
|
28
29
|
renderItem,
|
29
30
|
listItemConfigs,
|
30
31
|
labelKey = "userName",
|
@@ -34,12 +35,15 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
34
35
|
avataProps = {},
|
35
36
|
disabledStyle,
|
36
37
|
customItemNode,
|
38
|
+
labelInValue = false,
|
39
|
+
isClearable = false,
|
37
40
|
...selectProps
|
38
41
|
}) => {
|
39
42
|
const [search, setSearch] = useState("");
|
40
43
|
const [loading, setLoading] = useState(false);
|
41
44
|
const [loadingMore, setLoadingMore] = useState(false);
|
42
|
-
const [list, setList] = useState<
|
45
|
+
const [list, setList] = useState<LabelValue[]>([]);
|
46
|
+
const [cacheDeafultList, setCacheDeafultList] = useState<LabelValue[]>([]);
|
43
47
|
const [pagination, setPagination] = useState<ScrollPagination>({
|
44
48
|
pageNum: 1,
|
45
49
|
pageSize: 10,
|
@@ -47,12 +51,13 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
47
51
|
});
|
48
52
|
const selectInstance = useRef<any>(null);
|
49
53
|
const total = useRef(0);
|
54
|
+
const initSearch = useRef(true);
|
50
55
|
|
51
56
|
const debounceLoadData = debounce(async (isScrollLoad = false) => {
|
52
57
|
try {
|
53
58
|
isScrollLoad ? setLoadingMore(true) : setLoading(true);
|
54
59
|
|
55
|
-
const result = await
|
60
|
+
const result = await loadData(search, {
|
56
61
|
...pagination,
|
57
62
|
pageNum: isScrollLoad ? pagination.pageNum + 1 : 1,
|
58
63
|
});
|
@@ -60,7 +65,7 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
60
65
|
|
61
66
|
total.current = resPagination.total;
|
62
67
|
|
63
|
-
const transformatList:
|
68
|
+
const transformatList: LabelValue[] = transfromLabelInValueData(list, labelKey, valueKey);
|
64
69
|
|
65
70
|
setList((prev) => (isScrollLoad ? [...prev, ...transformatList] : transformatList));
|
66
71
|
} finally {
|
@@ -73,15 +78,15 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
73
78
|
const isNearBottom = scrollHeight - scrollTop - clientHeight < 30;
|
74
79
|
|
75
80
|
if (isNearBottom && !loadingMore && list.length <= total.current) {
|
76
|
-
await
|
81
|
+
await getData(true);
|
77
82
|
setPagination((pre) => ({ ...pre, pageNum: pagination.pageNum + 1 }));
|
78
83
|
}
|
79
84
|
};
|
80
85
|
|
81
|
-
const
|
86
|
+
const getData = useCallback(debounceLoadData, [search, pagination, loadData]);
|
82
87
|
|
83
88
|
// 处理滚动加载
|
84
|
-
const handleScroll = useCallback(debounceHandleScroll, [loadingMore, loadData]);
|
89
|
+
const handleScroll = useCallback(debounceHandleScroll, [search, loadingMore, loadData]);
|
85
90
|
|
86
91
|
// 重置list滚动条
|
87
92
|
const resetListScroll = () => {
|
@@ -96,36 +101,61 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
96
101
|
setPagination((prev) => ({ ...prev, pageNum: 1 }));
|
97
102
|
}, 1000);
|
98
103
|
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
+
};
|
102
114
|
|
103
115
|
// 处理选项点击事件
|
104
|
-
const handleItemClick = (item:
|
116
|
+
const handleItemClick = (item: LabelValue) => {
|
105
117
|
const { value: currentValue, mode, onChange } = selectProps;
|
106
|
-
const itemId = item.
|
118
|
+
const itemId = item.value;
|
107
119
|
|
108
120
|
// 多选模式处理
|
109
121
|
const handleMultipleSelection = () => {
|
110
|
-
const selectedOptions = Array.isArray(currentValue) ? currentValue : [];
|
111
|
-
|
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);
|
112
133
|
|
113
134
|
return isSelected
|
114
|
-
? selectedOptions.filter((opt) => opt
|
135
|
+
? selectedOptions.filter((opt) => opt !== itemId) // 移除已选项
|
115
136
|
: [...selectedOptions, item]; // 添加新选项
|
116
137
|
};
|
117
138
|
|
118
139
|
// 单选模式处理
|
119
140
|
const handleSingleSelection = () => {
|
120
|
-
const isCurrentSelected = (currentValue as
|
141
|
+
const isCurrentSelected = (currentValue as LabelValue)?.value === itemId;
|
121
142
|
return isCurrentSelected ? undefined : item;
|
122
143
|
};
|
123
144
|
|
145
|
+
let resultValue: string | number | any[] | LabelValue;
|
146
|
+
|
124
147
|
// 执行模式对应处理
|
125
148
|
const newValue = mode === "multiple" ? handleMultipleSelection() : handleSingleSelection();
|
126
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
|
+
|
127
157
|
// 触发变更回调
|
128
|
-
onChange?.(
|
158
|
+
onChange?.(resultValue, currentValue as any);
|
129
159
|
|
130
160
|
// 单选模式自动关闭下拉框
|
131
161
|
if (mode !== "multiple" && selectInstance.current) selectInstance.current.blur();
|
@@ -137,19 +167,39 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
137
167
|
return defaultListItemConfigs;
|
138
168
|
}, [listItemConfigs]);
|
139
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
|
+
|
140
185
|
// 默认渲染项(增加选中状态和点击处理)
|
141
186
|
const defaultRenderItem = useCallback(
|
142
|
-
(item:
|
187
|
+
(item: LabelValue) => {
|
143
188
|
const { mode, value: selectedValue } = selectProps;
|
144
|
-
const {
|
189
|
+
const { value, [disabledKey]: isDisabled = false } = item;
|
145
190
|
// 选中状态判断
|
146
191
|
const getSelectionStatus = () => {
|
147
192
|
if (mode === "multiple") {
|
148
193
|
if (!Array.isArray(selectedValue)) return false;
|
149
|
-
const selectedValues = (selectedValue || [])?.map((opt) => (isObject(opt) ? opt?.value ?? opt?.
|
150
|
-
return selectedValues.includes(
|
194
|
+
const selectedValues = (selectedValue || [])?.map((opt) => (isObject(opt) ? opt?.value ?? opt?.value : opt));
|
195
|
+
return selectedValues.includes(value);
|
196
|
+
}
|
197
|
+
|
198
|
+
if (Array.isArray(cacheDeafultList)) {
|
199
|
+
return cacheDeafultList.map((x) => x.value).includes(value);
|
151
200
|
}
|
152
|
-
|
201
|
+
|
202
|
+
return isObject(selectedValue) ? (selectedValue as LabelValue)?.value === value : selectedValue === value;
|
153
203
|
};
|
154
204
|
|
155
205
|
// 禁用样式生成
|
@@ -182,9 +232,9 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
182
232
|
);
|
183
233
|
|
184
234
|
return (
|
185
|
-
<List.Item key={
|
235
|
+
<List.Item key={value} onClick={() => !isDisabled && handleItemClick(item)} style={generateStyle(isDisabled)}>
|
186
236
|
{customItemNode || (
|
187
|
-
<div className="abt-user-item">
|
237
|
+
<div className="abt-user-item" key={item?.value}>
|
188
238
|
<div className="abt-user-item-avatar">{avatarComponent}</div>
|
189
239
|
{userInfoContent}
|
190
240
|
</div>
|
@@ -192,7 +242,7 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
192
242
|
</List.Item>
|
193
243
|
);
|
194
244
|
},
|
195
|
-
[selectProps.value],
|
245
|
+
[selectProps.value, list?.length],
|
196
246
|
);
|
197
247
|
|
198
248
|
// 自定义下拉内容
|
@@ -216,8 +266,8 @@ const RemoteSelect: React.FC<RemoteSelectProps> = ({
|
|
216
266
|
<Select
|
217
267
|
{...selectProps}
|
218
268
|
ref={selectInstance}
|
269
|
+
onDropdownVisibleChange={handleOnDropdownVisibleChange}
|
219
270
|
showSearch
|
220
|
-
labelInValue
|
221
271
|
onSearch={handleSearch}
|
222
272
|
filterOption={false}
|
223
273
|
dropdownRender={dropdownRender}
|
@@ -2,41 +2,40 @@ import type { SelectProps } from "antd/lib/select";
|
|
2
2
|
import React from "react";
|
3
3
|
import type { AvatarProps } from "antd/lib/avatar";
|
4
4
|
|
5
|
-
export interface Person {
|
6
|
-
userId: string | number;
|
7
|
-
userName: string;
|
8
|
-
avatar?: string;
|
9
|
-
email?: string;
|
10
|
-
description?: string;
|
11
|
-
disabled?: boolean;
|
12
|
-
[key: string]: any;
|
13
|
-
}
|
14
|
-
|
15
5
|
export interface ScrollPagination {
|
16
6
|
pageNum: number;
|
17
7
|
pageSize: number;
|
18
8
|
current?: number;
|
9
|
+
hasMore?: boolean;
|
19
10
|
total?: number;
|
20
11
|
}
|
21
12
|
|
22
|
-
export type
|
13
|
+
export type LabelValue = {
|
23
14
|
label: string;
|
24
15
|
value: string | number;
|
25
16
|
[key: string]: any;
|
26
17
|
};
|
27
18
|
|
28
|
-
|
19
|
+
interface BaseSelectProps extends Omit<SelectProps<any>, "options" | "children"> {
|
29
20
|
/**
|
30
21
|
* @title 请求函数
|
31
22
|
* */
|
32
|
-
|
23
|
+
loadData: (
|
33
24
|
search: string,
|
34
25
|
pagination: ScrollPagination,
|
35
|
-
) => Promise<{ list:
|
26
|
+
) => Promise<{ list: LabelValue[]; pagination: ScrollPagination }>;
|
27
|
+
/**
|
28
|
+
* @title 查询函数
|
29
|
+
* */
|
30
|
+
searchData?: <T = Record<string, any>, U = any[]>(query: T) => Promise<U[]>;
|
36
31
|
/**
|
37
32
|
* @title 自定义列表项
|
38
33
|
* */
|
39
|
-
renderItem?: (item:
|
34
|
+
renderItem?: (item: LabelValue) => React.ReactNode;
|
35
|
+
/**
|
36
|
+
* @title 下拉框收起时是否清除数据
|
37
|
+
* */
|
38
|
+
isClearable?: boolean;
|
40
39
|
/**
|
41
40
|
* @title 分页参数
|
42
41
|
* */
|
@@ -45,10 +44,7 @@ export interface RemoteSelectProps extends Omit<SelectProps<any>, "options" | "c
|
|
45
44
|
* @title antd头像组件props
|
46
45
|
* */
|
47
46
|
avataProps?: AvatarProps;
|
48
|
-
|
49
|
-
* @title 下拉框值
|
50
|
-
* */
|
51
|
-
value: LableValue[] | LableValue;
|
47
|
+
mode: "multiple" | "tags";
|
52
48
|
/**
|
53
49
|
* @title 列表项内部展示的字段内容
|
54
50
|
* */
|
@@ -74,3 +70,23 @@ export interface RemoteSelectProps extends Omit<SelectProps<any>, "options" | "c
|
|
74
70
|
* */
|
75
71
|
customItemNode?: React.ReactNode;
|
76
72
|
}
|
73
|
+
|
74
|
+
// 多选模式
|
75
|
+
export interface RemoteSelectMultipleProps extends BaseSelectProps {
|
76
|
+
mode: "multiple";
|
77
|
+
/**
|
78
|
+
* @title 下拉框值
|
79
|
+
* */
|
80
|
+
value?: LabelValue[] | Array<string | number>;
|
81
|
+
}
|
82
|
+
|
83
|
+
// 单选模式
|
84
|
+
export interface RemoteSelectTagsProps extends BaseSelectProps {
|
85
|
+
mode: "tags";
|
86
|
+
/**
|
87
|
+
* @title 下拉框值
|
88
|
+
* */
|
89
|
+
value?: LabelValue | string | number;
|
90
|
+
}
|
91
|
+
|
92
|
+
export type RemoteSelectProps = RemoteSelectMultipleProps | RemoteSelectTagsProps;
|
@@ -7,7 +7,7 @@ import { handlePreviewUrls } from "../Upload/common/OfflineUpload";
|
|
7
7
|
import "./index.less";
|
8
8
|
import { Spin, message } from "antd";
|
9
9
|
import { connect, mapProps, observer, useField } from "@formily/react";
|
10
|
-
|
10
|
+
import { merge } from "lodash";
|
11
11
|
interface AnyObject {
|
12
12
|
[key: string]: any;
|
13
13
|
}
|
@@ -136,9 +136,37 @@ export const RichEditor = observer(function (props: PropsType, parentRef) {
|
|
136
136
|
customUpload,
|
137
137
|
},
|
138
138
|
},
|
139
|
-
...editorConfig,
|
139
|
+
...merge(editorConfig, {
|
140
|
+
MENU_CONF: {
|
141
|
+
uploadImage: {
|
142
|
+
// form-data fieldName ,默认值 'wangeditor-uploaded-image'
|
143
|
+
fieldName: "wangeditor-uploaded-image",
|
144
|
+
// 单个文件的最大体积限制,默认为 2M
|
145
|
+
maxFileSize: 1 * 1024 * 1024, // 1M
|
146
|
+
// 最多可上传几个文件,默认为 100
|
147
|
+
maxNumberOfFiles: 1,
|
148
|
+
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
|
149
|
+
allowedFileTypes: ["image/*"],
|
150
|
+
// 跨域是否传递 cookie ,默认为 false
|
151
|
+
withCredentials: false,
|
152
|
+
// 超时时间,默认为 10 秒
|
153
|
+
timeout: 5 * 1000, // 5 秒
|
154
|
+
customUpload,
|
155
|
+
},
|
156
|
+
uploadVideo: {
|
157
|
+
// form-data fieldName ,默认值 'wangeditor-uploaded-image'
|
158
|
+
fieldName: "wangeditor-uploaded-video",
|
159
|
+
// 最多可上传几个文件,默认为 100
|
160
|
+
maxNumberOfFiles: 1,
|
161
|
+
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
|
162
|
+
allowedFileTypes: ["video/*"],
|
163
|
+
// 跨域是否传递 cookie ,默认为 false
|
164
|
+
withCredentials: false,
|
165
|
+
customUpload,
|
166
|
+
},
|
167
|
+
},
|
168
|
+
}),
|
140
169
|
};
|
141
|
-
|
142
170
|
const onEditorChange = (e) => {
|
143
171
|
const content = e.getHtml();
|
144
172
|
setHtml(content);
|