@hzab/form-render 1.4.1-beta → 1.5.0-beta
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 +3 -2
- package/package.json +1 -1
- package/src/common/schema-handler.ts +84 -29
- package/src/components/ArrayBase/index.tsx +315 -0
- package/src/components/ArrayBase/style.less +87 -0
- package/src/components/ArrayBase/style.ts +2 -0
- package/src/components/ArrayCards/index.tsx +11 -18
- package/src/components/ArrayTable/index.tsx +6 -14
- package/src/components/DatePicker/index.tsx +1 -2
- package/src/components/Upload/README.md +28 -20
- package/src/components/Upload/common/customRequest.ts +34 -0
- package/src/components/Upload/common/fileName.ts +62 -0
- package/src/components/Upload/common/handleIOFileList.ts +302 -0
- package/src/components/Upload/common/nanoid.ts +7 -0
- package/src/components/Upload/common/ossUpload.js +9 -13
- package/src/components/Upload/common/utils.js +29 -13
- package/src/components/Upload/components/ItemList/index.tsx +2 -3
- package/src/components/Upload/components/PreviewModal/index.tsx +0 -1
- package/src/components/Upload/uploader-input.jsx +48 -12
- package/src/components/Upload/uploader.jsx +88 -83
- package/src/components/index.tsx +1 -0
- package/src/index.tsx +11 -8
@@ -3,7 +3,8 @@ import { Button, Modal, message } from "antd";
|
|
3
3
|
|
4
4
|
import ItemList from "./components/ItemList";
|
5
5
|
|
6
|
-
import { handleMaxCount
|
6
|
+
import { handleMaxCount } from "./common/utils";
|
7
|
+
import { handleInputFileList, handleOutputFileList } from "./common/handleIOFileList";
|
7
8
|
import { handleOssUpload } from "./common/ossUpload";
|
8
9
|
|
9
10
|
import "./uploader.less";
|
@@ -16,6 +17,26 @@ function Uploader(props) {
|
|
16
17
|
multiple,
|
17
18
|
// 模式字符串: all | select | image | video 或 数组 select | image | video 组合:如:['select', 'image']
|
18
19
|
mode = "select",
|
20
|
+
/** 上传模式
|
21
|
+
* 阿里云 oss: oss
|
22
|
+
* 内部定制私有化部署: offline
|
23
|
+
* 自定义: custom
|
24
|
+
* 文件: file
|
25
|
+
*/
|
26
|
+
uploadMode: _uploadMode = "oss",
|
27
|
+
/**
|
28
|
+
* 数据格式配置
|
29
|
+
*/
|
30
|
+
fileListConf = {
|
31
|
+
/** 列表数据模式 "array" | "jsonStr" | "splitStr" */
|
32
|
+
listMode: "array",
|
33
|
+
/** 字符串列表数据模式下的分隔符 */
|
34
|
+
split: ", ",
|
35
|
+
/** 子项数据模式 "object" | "file" | "url" | "jsonStr" */
|
36
|
+
itemMode: "url",
|
37
|
+
/** 预览配置参数 */
|
38
|
+
previewConfig: {},
|
39
|
+
},
|
19
40
|
onChange,
|
20
41
|
accept,
|
21
42
|
disabled,
|
@@ -26,7 +47,7 @@ function Uploader(props) {
|
|
26
47
|
maxSize,
|
27
48
|
maxCount = 1,
|
28
49
|
// 是否使用 oss 上传文件
|
29
|
-
isOssUpload,
|
50
|
+
isOssUpload = true,
|
30
51
|
// 是否使用字符串结果
|
31
52
|
isStrRes,
|
32
53
|
// maxCount === 1 时,结果是否自动去除数组嵌套
|
@@ -34,7 +55,22 @@ function Uploader(props) {
|
|
34
55
|
ossUrl,
|
35
56
|
ossOpt,
|
36
57
|
onCountExceed,
|
58
|
+
isFileJson,
|
59
|
+
isFileObj,
|
37
60
|
} = props;
|
61
|
+
|
62
|
+
// 参数归一化,兼容老参数
|
63
|
+
const uploadMode = isOssUpload === true ? "oss" : _uploadMode;
|
64
|
+
if (isStrRes === true && isFileObj === false) {
|
65
|
+
fileListConf.itemMode = "url";
|
66
|
+
}
|
67
|
+
if (isFileJson === true) {
|
68
|
+
fileListConf.listMode = "jsonStr";
|
69
|
+
}
|
70
|
+
if (isFileObj === true) {
|
71
|
+
fileListConf.itemMode = "object";
|
72
|
+
}
|
73
|
+
|
38
74
|
const [loading, setLoading] = useState(false);
|
39
75
|
const [fileList, setFileList] = useState(handleInputFileList(value, maxCount));
|
40
76
|
useEffect(() => {
|
@@ -59,7 +95,7 @@ function Uploader(props) {
|
|
59
95
|
return it;
|
60
96
|
});
|
61
97
|
// 处理 oss 逻辑
|
62
|
-
if (
|
98
|
+
if (uploadMode === "oss") {
|
63
99
|
setLoading(true);
|
64
100
|
files = await handleOssUpload(files, {
|
65
101
|
axios: props.axios,
|
@@ -81,17 +117,17 @@ function Uploader(props) {
|
|
81
117
|
}
|
82
118
|
_files = handleMaxCount(files, maxCount);
|
83
119
|
setFileList(_files);
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
}
|
89
|
-
|
90
|
-
|
91
|
-
} else if (isResRemoveArr && maxCount === 1) {
|
120
|
+
|
121
|
+
// 处理出参格式
|
122
|
+
_files = handleOutputFileList(_files, {
|
123
|
+
...fileListConf,
|
124
|
+
});
|
125
|
+
|
126
|
+
if (isResRemoveArr && maxCount === 1) {
|
92
127
|
// maxCount 为1的时候返回结果去除数组
|
93
128
|
_files = _files[0];
|
94
129
|
}
|
130
|
+
|
95
131
|
onChange && onChange(_files);
|
96
132
|
}
|
97
133
|
|
@@ -126,7 +162,7 @@ function Uploader(props) {
|
|
126
162
|
<input {...inputProps} ref={uploaderRef}></input>
|
127
163
|
<ItemList
|
128
164
|
fileList={fileList}
|
129
|
-
baseUrl={
|
165
|
+
baseUrl={uploadMode === "oss" ? baseUrl : ""}
|
130
166
|
disabled={disabled}
|
131
167
|
readOnly={readOnly}
|
132
168
|
onItemDel={onItemDel}
|
@@ -8,113 +8,122 @@ import {
|
|
8
8
|
} from "@ant-design/icons";
|
9
9
|
import { Button, Upload as AUpload, message } from "antd";
|
10
10
|
import React, { useEffect, useState } from "react";
|
11
|
-
import { nanoid } from "nanoid";
|
12
11
|
|
13
|
-
import { handleInputFileList,
|
14
|
-
import LUploadOss from "./common/ossUpload";
|
12
|
+
import { handleInputFileList, handleOutputFileList } from "./common/handleIOFileList";
|
15
13
|
|
16
14
|
import PreviewModal, { hasPreviewRender, hasPreviewMedium } from "./components/PreviewModal";
|
15
|
+
import { getOssUploadRequest, getOfflineUploadRequest } from "./common/customRequest";
|
17
16
|
|
18
17
|
import "./uploader.less";
|
19
18
|
|
19
|
+
const defaultFileListConf = {
|
20
|
+
/** 列表数据模式 "array" | "jsonStr" | "splitStr" */
|
21
|
+
listMode: "array",
|
22
|
+
/** 字符串列表数据模式下的分隔符 */
|
23
|
+
split: ", ",
|
24
|
+
/** 子项数据模式 "object" | "file" | "url" | "jsonStr" */
|
25
|
+
itemMode: "url",
|
26
|
+
};
|
27
|
+
|
20
28
|
export function Uploader({ onChange, ...props }) {
|
21
29
|
const {
|
30
|
+
/** 上传模式
|
31
|
+
* 阿里云 oss: oss
|
32
|
+
* 内部定制私有化部署: offline
|
33
|
+
* 自定义: custom
|
34
|
+
* 文件: file
|
35
|
+
*/
|
36
|
+
uploadMode: _uploadMode = "oss",
|
22
37
|
name = "file",
|
23
38
|
multiple = false,
|
24
39
|
// 媒体类型限制,当url地址不带类型后缀时传入,一个组件最好只传一种媒体类型,例如image/*,若混传,例如video/*,image/*这种,在预览时只会预览第一种。
|
25
40
|
accept,
|
26
41
|
value,
|
27
|
-
btnText = "上传",
|
42
|
+
btnText = props.textContent || "上传",
|
28
43
|
maxCount = Infinity,
|
29
44
|
// 文件大小限制
|
30
45
|
maxSize,
|
31
46
|
listType = "text",
|
32
47
|
disabled = false,
|
33
48
|
readOnly = false,
|
34
|
-
|
49
|
+
ossServerUrl = "/api/v1/user/oss/getWebOssConfig",
|
35
50
|
// 是否使用 oss 上传文件
|
36
51
|
isOssUpload = true,
|
37
52
|
// 是否使用字符串结果
|
38
53
|
isStrRes,
|
39
54
|
// maxCount === 1 时,结果是否自动去除数组嵌套
|
40
55
|
isResRemoveArr = true,
|
41
|
-
|
56
|
+
/** 预览配置参数 */
|
57
|
+
previewConfig = {},
|
42
58
|
onCountExceed,
|
43
59
|
templateUrl,
|
44
60
|
beforeUploadCheck,
|
45
61
|
templateDownloadText = "模板下载",
|
46
62
|
isFileObj = false,
|
47
63
|
isFileJson = false,
|
48
|
-
UploadOss,
|
49
|
-
previewConfig,
|
50
64
|
} = props;
|
51
65
|
|
66
|
+
/**
|
67
|
+
* 数据格式配置
|
68
|
+
*/
|
69
|
+
const fileListConf = { ...defaultFileListConf, ...props.fileListConf };
|
70
|
+
|
71
|
+
// 参数归一化,兼容老参数
|
72
|
+
const uploadMode = isOssUpload === true ? "oss" : _uploadMode;
|
73
|
+
if (isStrRes === true && isFileObj === false) {
|
74
|
+
fileListConf.itemMode = "url";
|
75
|
+
}
|
76
|
+
if (isFileJson === true) {
|
77
|
+
fileListConf.listMode = "jsonStr";
|
78
|
+
}
|
79
|
+
if (isFileObj === true) {
|
80
|
+
fileListConf.itemMode = "object";
|
81
|
+
}
|
82
|
+
|
52
83
|
const [fileList, setFileList] = useState([]);
|
53
84
|
const [previewFile, setPreviewFile] = useState();
|
54
85
|
const [checking, setChecking] = useState(false);
|
55
86
|
|
56
|
-
|
87
|
+
/**
|
88
|
+
* Upload onChange 函数,上传中、完成、失败都会调用这个函数。
|
89
|
+
* @param {*} param0
|
90
|
+
* @returns
|
91
|
+
*/
|
92
|
+
const handleChange = async ({ fileList, file, event }) => {
|
57
93
|
setFileList(fileList);
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
let _file = _files[i];
|
63
|
-
if (_file?.originFileObj) {
|
64
|
-
_file = _file?.originFileObj;
|
65
|
-
}
|
66
|
-
// 从文件对象中获取 url,ossPromise 为上传中的文件,兼容 file 为字符串的情况
|
94
|
+
// 上传中不进行处理,结局 ossPromise 不存在的问题
|
95
|
+
if (file.status !== "done") {
|
96
|
+
return;
|
97
|
+
}
|
67
98
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
99
|
+
let _files = [...fileList];
|
100
|
+
// 取出异步 url
|
101
|
+
for (let i = 0; i < _files.length; i++) {
|
102
|
+
let _file = _files[i]?.originFileObj || _files[i];
|
103
|
+
if (!_file.url) {
|
104
|
+
_file.url = (await _file.ossPromise) || _file.ossUrl || typeof _file === "string" ? _file : "";
|
105
|
+
_files[i].url = _file.url;
|
75
106
|
}
|
76
107
|
}
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
108
|
+
|
109
|
+
// 处理出参格式
|
110
|
+
_files = handleOutputFileList(_files, {
|
111
|
+
...fileListConf,
|
112
|
+
});
|
113
|
+
|
114
|
+
if (isResRemoveArr && maxCount === 1 && Array.isArray(_files)) {
|
81
115
|
// maxCount 为 1 的时候返回结果去除数组
|
82
116
|
_files = _files[0];
|
83
117
|
}
|
84
|
-
|
118
|
+
|
119
|
+
onChange && onChange?.(_files);
|
85
120
|
};
|
86
121
|
|
87
122
|
useEffect(() => {
|
88
123
|
if (!value) {
|
89
124
|
return;
|
90
125
|
}
|
91
|
-
|
92
|
-
let _list = [];
|
93
|
-
try {
|
94
|
-
_list = JSON.parse(value);
|
95
|
-
} catch (e) {
|
96
|
-
_list = value;
|
97
|
-
}
|
98
|
-
|
99
|
-
if (typeof _list === "string") {
|
100
|
-
_list = value.split(",");
|
101
|
-
}
|
102
|
-
|
103
|
-
if (Array.isArray(_list)) {
|
104
|
-
_list = _list.map((res) => {
|
105
|
-
if (typeof res === "string") {
|
106
|
-
return {
|
107
|
-
name: getFileName(res),
|
108
|
-
status: "done",
|
109
|
-
uid: `rc-upload-${Date.now()}-${nanoid()}`,
|
110
|
-
url: res,
|
111
|
-
type: getFileType(res),
|
112
|
-
};
|
113
|
-
}
|
114
|
-
return res;
|
115
|
-
});
|
116
|
-
}
|
117
|
-
setFileList(handleInputFileList(_list, maxCount, isFileObj, isOssUpload, previewConfig));
|
126
|
+
setFileList(handleInputFileList(value, { ...fileListConf, maxCount, previewConfig }));
|
118
127
|
}, [value]);
|
119
128
|
|
120
129
|
const onRemove = (file) => {
|
@@ -122,6 +131,20 @@ export function Uploader({ onChange, ...props }) {
|
|
122
131
|
onChange && onChange(files);
|
123
132
|
};
|
124
133
|
|
134
|
+
// 自定义请求逻辑
|
135
|
+
let customRequest = props.customRequest;
|
136
|
+
if (uploadMode === "oss") {
|
137
|
+
customRequest = getOssUploadRequest({
|
138
|
+
...props,
|
139
|
+
ossServerUrl: ossServerUrl || props.ossUrl,
|
140
|
+
});
|
141
|
+
} else if (uploadMode === "offline") {
|
142
|
+
customRequest = getOfflineUploadRequest({
|
143
|
+
...props,
|
144
|
+
ossServerUrl: ossServerUrl || props.ossUrl,
|
145
|
+
});
|
146
|
+
}
|
147
|
+
|
125
148
|
const _uploadProps = {
|
126
149
|
iconRender: (e) => {
|
127
150
|
if (e?.type == "application/pdf") {
|
@@ -189,36 +212,18 @@ export function Uploader({ onChange, ...props }) {
|
|
189
212
|
}
|
190
213
|
setChecking(false);
|
191
214
|
},
|
192
|
-
|
193
|
-
customRequest: ({ action, data, file, filename, headers, method, onSuccess, onProgress, onError }) => {
|
194
|
-
const _UploadOss = UploadOss || LUploadOss;
|
195
|
-
const ossUpload = new _UploadOss({
|
196
|
-
serverUrl: ossUrl,
|
197
|
-
...ossOpt,
|
198
|
-
});
|
199
|
-
file.ossPromise = ossUpload
|
200
|
-
.upload(file, {
|
201
|
-
params: {
|
202
|
-
isPublic: 1,
|
203
|
-
...(props.params || {}),
|
204
|
-
},
|
205
|
-
})
|
206
|
-
.then((res) => {
|
207
|
-
file.url = res?.data?.data?.fileUrl ?? res?.data?.fileUrl ?? res?.fileUrl;
|
208
|
-
file.ossUrl = file.url;
|
209
|
-
onSuccess(file);
|
210
|
-
return isFileObj ? file : file.ossUrl;
|
211
|
-
})
|
212
|
-
.catch(onError);
|
213
|
-
}
|
214
|
-
} : {}),
|
215
|
+
customRequest,
|
215
216
|
onPreview(file) {
|
217
|
+
// TODO: 逻辑合并到数据处理中
|
216
218
|
const isShowPreview = hasPreviewRender(file) || hasPreviewMedium(props.accept);
|
219
|
+
const fileUrl = file.previewUrl;
|
217
220
|
if (isShowPreview) {
|
218
|
-
setPreviewFile({
|
221
|
+
setPreviewFile({
|
222
|
+
...file,
|
223
|
+
url: fileUrl,
|
224
|
+
});
|
219
225
|
} else {
|
220
|
-
|
221
|
-
window.open(imgUrl);
|
226
|
+
window.open(fileUrl);
|
222
227
|
}
|
223
228
|
},
|
224
229
|
...(props || {}),
|
package/src/components/index.tsx
CHANGED
package/src/index.tsx
CHANGED
@@ -26,8 +26,6 @@ import {
|
|
26
26
|
FormLayout,
|
27
27
|
FormTab,
|
28
28
|
FormCollapse,
|
29
|
-
ArrayTable,
|
30
|
-
ArrayCards,
|
31
29
|
} from "c-formily-antd";
|
32
30
|
import { Card, Slider, Rate } from "antd";
|
33
31
|
|
@@ -63,11 +61,18 @@ const antdComponents = {
|
|
63
61
|
FormLayout,
|
64
62
|
FormTab,
|
65
63
|
FormCollapse,
|
66
|
-
ArrayTable,
|
67
|
-
ArrayCards,
|
68
64
|
};
|
69
65
|
|
66
|
+
/** schema scope 解决父级无 schema Scope 导致 scope 对象刷新的问题 */
|
67
|
+
let _schemaScope = { _$tempData: {} };
|
68
|
+
|
70
69
|
const FormRender = forwardRef((props: any, parentRef) => {
|
70
|
+
if (props.schemaScope) {
|
71
|
+
_schemaScope = props.schemaScope;
|
72
|
+
}
|
73
|
+
if (!props.schemaScope?._$tempData) {
|
74
|
+
_schemaScope._$tempData = {};
|
75
|
+
}
|
71
76
|
const SchemaField = useCallback(
|
72
77
|
createSchemaField({
|
73
78
|
components: {
|
@@ -76,8 +81,6 @@ const FormRender = forwardRef((props: any, parentRef) => {
|
|
76
81
|
FormLayout,
|
77
82
|
FormTab,
|
78
83
|
FormCollapse,
|
79
|
-
ArrayTable,
|
80
|
-
ArrayCards,
|
81
84
|
FormItem,
|
82
85
|
DatePicker,
|
83
86
|
Checkbox,
|
@@ -102,7 +105,7 @@ const FormRender = forwardRef((props: any, parentRef) => {
|
|
102
105
|
...customComponents,
|
103
106
|
...props.components,
|
104
107
|
},
|
105
|
-
scope:
|
108
|
+
scope: _schemaScope,
|
106
109
|
}),
|
107
110
|
[],
|
108
111
|
);
|
@@ -150,7 +153,7 @@ const FormRender = forwardRef((props: any, parentRef) => {
|
|
150
153
|
|
151
154
|
const schema = useMemo(() => {
|
152
155
|
return bindOnChange(props.schema, {
|
153
|
-
schemaScope:
|
156
|
+
schemaScope: _schemaScope,
|
154
157
|
onChange: props.onChange,
|
155
158
|
formRender,
|
156
159
|
});
|