@hzab/form-render 1.4.0 → 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.
@@ -3,7 +3,8 @@ import { Button, Modal, message } from "antd";
3
3
 
4
4
  import ItemList from "./components/ItemList";
5
5
 
6
- import { handleMaxCount, handleInputFileList } from "./common/utils";
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 (isOssUpload) {
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
- if (isOssUpload && isStrRes) {
85
- _files = _files?.map((file) => {
86
- return file.url || file.ossUrl;
87
- });
88
- }
89
- if (!_files || _files.length <= 0) {
90
- _files = undefined;
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={isOssUpload ? 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, getFileName, getFileType, mergePreviewConfig } from "./common/utils";
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
- ossUrl = "/api/v1/user/oss/getWebOssConfig",
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
- ossOpt,
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
- const handleChange = async ({ fileList }) => {
87
+ /**
88
+ * Upload onChange 函数,上传中、完成、失败都会调用这个函数。
89
+ * @param {*} param0
90
+ * @returns
91
+ */
92
+ const handleChange = async ({ fileList, file, event }) => {
57
93
  setFileList(fileList);
58
- let _files = [...fileList];
59
- // 处理提交结果为字符串的情况
60
- if (isOssUpload || isStrRes) {
61
- for (let i = 0; i < _files.length; i++) {
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
- if (isFileObj) {
69
- let { ossUrl, url, uid, name, type } = (await _file.ossPromise) || _file;
70
- _files[i] = { ossUrl, url: isOssUpload ? mergePreviewConfig(ossUrl, previewConfig) : url, uid, name, type };
71
- } else {
72
- let str = (await _file.ossPromise) || _file.url || _file.ossUrl || _file;
73
- _files[i] = str;
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
- if (!_files || _files.length <= 0) {
79
- _files = undefined;
80
- } else if (isResRemoveArr && maxCount === 1) {
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
- onChange && onChange?.(isFileJson ? JSON.stringify(_files) : _files);
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
- ...(isOssUpload ? {
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({ ...file, url: isOssUpload ? mergePreviewConfig(file?.ossUrl || file?.url, previewConfig) : file.url });
221
+ setPreviewFile({
222
+ ...file,
223
+ url: fileUrl,
224
+ });
219
225
  } else {
220
- const imgUrl = isOssUpload ? mergePreviewConfig(file?.ossUrl || file?.url, previewConfig) : file.url
221
- window.open(imgUrl);
226
+ window.open(fileUrl);
222
227
  }
223
228
  },
224
229
  ...(props || {}),
@@ -1,5 +1,8 @@
1
1
  import Text from "./Text";
2
2
 
3
+ export * from "./ArrayBase";
4
+ export * from "./ArrayTable";
5
+ export * from "./ArrayCards";
3
6
  export * from "./Upload";
4
7
 
5
8
  export * from "./UserSelect";
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: { ...props.schemaScope },
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: props.schemaScope,
156
+ schemaScope: _schemaScope,
154
157
  onChange: props.onChange,
155
158
  formRender,
156
159
  });