@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
@@ -1,4 +1,4 @@
|
|
1
|
-
import React, { Fragment, useState, useRef, useEffect, createContext, useContext, useCallback
|
1
|
+
import React, { Fragment, useState, useRef, useEffect, createContext, useContext, useCallback } from "react";
|
2
2
|
import { Table, Pagination, Space, Select, Badge } from "antd";
|
3
3
|
import { PaginationProps } from "antd/lib/pagination";
|
4
4
|
import { TableProps, ColumnProps } from "antd/lib/table";
|
@@ -9,8 +9,7 @@ import { useField, observer, useFieldSchema, RecursionField, ReactFC } from "@fo
|
|
9
9
|
import { isArr, isBool, isFn } from "@formily/shared";
|
10
10
|
import { Schema } from "@formily/json-schema";
|
11
11
|
import { usePrefixCls, SortableContainer, SortableElement } from "c-formily-antd/lib/__builtins__";
|
12
|
-
import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from "
|
13
|
-
import { nanoid } from "nanoid";
|
12
|
+
import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from "../ArrayBase";
|
14
13
|
|
15
14
|
interface ObservableColumnSource {
|
16
15
|
field: GeneralField;
|
@@ -116,6 +115,7 @@ const useArrayTableColumns = (
|
|
116
115
|
if (!isColumnComponent(schema)) return buf;
|
117
116
|
return buf.concat({
|
118
117
|
...columnProps,
|
118
|
+
// ! key 用于排序,必须是序号
|
119
119
|
key,
|
120
120
|
dataIndex: name,
|
121
121
|
render: (value: any, record: any) => {
|
@@ -290,23 +290,14 @@ export const ArrayTable: ComposedArrayTable = observer((props) => {
|
|
290
290
|
const ref = useRef<HTMLDivElement>();
|
291
291
|
const field = useField<ArrayField>();
|
292
292
|
const prefixCls = usePrefixCls("formily-array-table");
|
293
|
-
const dataSource =
|
294
|
-
return Array.isArray(field.value)
|
295
|
-
? field.value?.slice().map((item) => {
|
296
|
-
return {
|
297
|
-
...item,
|
298
|
-
_key: item?.id || nanoid(),
|
299
|
-
};
|
300
|
-
})
|
301
|
-
: [];
|
302
|
-
}, [field.value.length]);
|
293
|
+
const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
|
303
294
|
const sources = useArrayTableSources();
|
304
295
|
const columns = useArrayTableColumns(dataSource, field, sources);
|
305
296
|
const pagination = isBool(props.pagination) ? { showPagination: props.pagination } : props.pagination;
|
306
297
|
const addition = useAddition();
|
307
298
|
const { onAdd, onCopy, onRemove, onMoveDown, onMoveUp } = props;
|
308
299
|
const defaultRowKey = (record: any) => {
|
309
|
-
return record
|
300
|
+
return dataSource.indexOf(record);
|
310
301
|
};
|
311
302
|
const addTdStyles = (id: number) => {
|
312
303
|
const node = ref.current?.querySelector(`.${prefixCls}-row-${id}`);
|
@@ -372,6 +363,7 @@ export const ArrayTable: ComposedArrayTable = observer((props) => {
|
|
372
363
|
name: column.name,
|
373
364
|
schema: column.schema,
|
374
365
|
onlyRenderSelf: true,
|
366
|
+
// ! key 用于排序,必须是序号
|
375
367
|
key,
|
376
368
|
});
|
377
369
|
})}
|
@@ -76,12 +76,11 @@ DatePicker.RangePicker = connect(
|
|
76
76
|
};
|
77
77
|
|
78
78
|
const handleOnChange = (dates, dateStrings) => {
|
79
|
-
|
80
79
|
props.onChange && props.onChange(dates);
|
81
80
|
if (!isSplitTimes) return;
|
82
81
|
if (!dates || !dates.length) {
|
83
82
|
handleOnConfirm(undefined, undefined);
|
84
|
-
return
|
83
|
+
return;
|
85
84
|
}
|
86
85
|
|
87
86
|
const startTime = dates[0];
|
@@ -12,26 +12,34 @@
|
|
12
12
|
- 参数参考 antd upload 组件
|
13
13
|
- props 参数 会覆盖 antd upload 组件已有配置项
|
14
14
|
|
15
|
-
| 参数 | 类型 | 必填 | 默认值 | 说明
|
16
|
-
| -------------------- | -------- | ---- | -------- |
|
17
|
-
| name | string | 否 | file | 文件名
|
18
|
-
| accept | string | 否 | - | 接受上传的文件类型, 详见 input accept Attribute
|
19
|
-
| listType | string | 否 | text | 上传列表的内建样式,支持三种基本样式 text, picture 和 picture-card
|
20
|
-
| maxCount | number | 否 | - | 限制上传数量。当为 1 时,始终用最新上传的文件代替当前文件
|
21
|
-
| maxSize | number | 否 | - | 限制上传的文件大小。
|
22
|
-
| multiple | boolean | 否 | - | 是否支持多选文件,ie10+ 支持。开启后按住 ctrl 可选择多个文件
|
23
|
-
| disabled | boolean | 否 | false | 禁用状态
|
24
|
-
| readOnly | boolean | 否 | false | 只读状态
|
25
|
-
| isResRemoveArr | boolean | 否 | false | maxCount === 1 时,结果是否自动去除数组嵌套
|
26
|
-
|
|
27
|
-
|
|
28
|
-
| ossOpt | Object | 否 | - | oss 上传的配置参数
|
29
|
-
|
|
30
|
-
| previewConfig | Object | 否 | {} |
|
31
|
-
| onCountExceed | Function | 否 | - | 数量超出 maxCount 时的回调
|
32
|
-
| beforeUploadCheck | Function | 否 | - | 追加的 beforeUpload 检查,返回 Promise.reject() 进行拦截
|
33
|
-
| templateUrl | string | 否 | - | 模板下载的地址
|
34
|
-
| templateDownloadText | string | 否 | 模板下载 | 模板下载按钮的文案
|
15
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
16
|
+
| -------------------- | -------- | ---- | -------- | ------------------------------------------------------------------------------------ |
|
17
|
+
| name | string | 否 | file | 文件名 |
|
18
|
+
| accept | string | 否 | - | 接受上传的文件类型, 详见 input accept Attribute |
|
19
|
+
| listType | string | 否 | text | 上传列表的内建样式,支持三种基本样式 text, picture 和 picture-card |
|
20
|
+
| maxCount | number | 否 | - | 限制上传数量。当为 1 时,始终用最新上传的文件代替当前文件 |
|
21
|
+
| maxSize | number | 否 | - | 限制上传的文件大小。 |
|
22
|
+
| multiple | boolean | 否 | - | 是否支持多选文件,ie10+ 支持。开启后按住 ctrl 可选择多个文件 |
|
23
|
+
| disabled | boolean | 否 | false | 禁用状态 |
|
24
|
+
| readOnly | boolean | 否 | false | 只读状态 |
|
25
|
+
| isResRemoveArr | boolean | 否 | false | maxCount === 1 时,结果是否自动去除数组嵌套 |
|
26
|
+
| uploadMode | string | 否 | oss | 上传模式: 阿里云 oss: oss, 内部定制私有化部署: offline, 文件: file, 自定义: custom, |
|
27
|
+
| ossServerUrl | string | 否 | - | 获取 oss 参数的接口,默认 /api/v1/user/oss/getWebOssConfig |
|
28
|
+
| ossOpt | Object | 否 | - | oss 上传的配置参数 |
|
29
|
+
| fileListConf | Object | 否 | {} | 结果数据配置 |
|
30
|
+
| previewConfig | Object | 否 | {} | 预览场景的配置 |
|
31
|
+
| onCountExceed | Function | 否 | - | 数量超出 maxCount 时的回调 |
|
32
|
+
| beforeUploadCheck | Function | 否 | - | 追加的 beforeUpload 检查,返回 Promise.reject() 进行拦截 |
|
33
|
+
| templateUrl | string | 否 | - | 模板下载的地址 |
|
34
|
+
| templateDownloadText | string | 否 | 模板下载 | 模板下载按钮的文案 |
|
35
|
+
|
36
|
+
### fileListConf 结果数据配置
|
37
|
+
|
38
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
39
|
+
| -------- | ------ | ---- | ------ | ---------------------------- | --------- | ---------- | --------- |
|
40
|
+
| listMode | string | 否 | array | 列表数据模式 "array" | "jsonStr" | "splitStr" |
|
41
|
+
| split | string | 否 | ", " | 字符串列表数据模式下的分隔符 |
|
42
|
+
| itemMode | string | 否 | "url" | 子项数据模式 "object" | "file" | "url" | "jsonStr" |
|
35
43
|
|
36
44
|
### ossOpt
|
37
45
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import LUploadOss from "./ossUpload";
|
2
|
+
|
3
|
+
export const getOfflineUploadRequest = (props) => {
|
4
|
+
return ({ action, data, file, filename, headers, method, onSuccess, onProgress, onError }) => {
|
5
|
+
// TODO: 处理请求逻辑
|
6
|
+
// file.ossPromise = ;
|
7
|
+
onSuccess(file);
|
8
|
+
};
|
9
|
+
};
|
10
|
+
|
11
|
+
export const getOssUploadRequest = (opt) => {
|
12
|
+
const { UploadOss, ossServerUrl, ossOpt, params } = opt || {};
|
13
|
+
return ({ action, data, file, filename, headers, method, onSuccess, onProgress, onError }) => {
|
14
|
+
const _UploadOss = UploadOss || LUploadOss;
|
15
|
+
const ossUpload = new _UploadOss({
|
16
|
+
serverUrl: ossServerUrl,
|
17
|
+
...ossOpt,
|
18
|
+
});
|
19
|
+
file.ossPromise = ossUpload
|
20
|
+
.upload(file, {
|
21
|
+
params: {
|
22
|
+
isPublic: 1,
|
23
|
+
...(params || {}),
|
24
|
+
},
|
25
|
+
})
|
26
|
+
.then((res) => {
|
27
|
+
file.url = res?.data?.data?.fileUrl ?? res?.data?.fileUrl ?? res?.fileUrl;
|
28
|
+
file.ossUrl = file.url;
|
29
|
+
onSuccess(file);
|
30
|
+
return file;
|
31
|
+
})
|
32
|
+
.catch(onError);
|
33
|
+
};
|
34
|
+
};
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { nanoid } from "./nanoid";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 从 url 获取文件名称,不包含后缀
|
5
|
+
* @param {*} fileName
|
6
|
+
* @param {*} str
|
7
|
+
* @returns
|
8
|
+
*/
|
9
|
+
export function getFileName(fileUrl) {
|
10
|
+
const res = fileUrl?.match(/([^\/]+?)\.[^\/\.]+$/);
|
11
|
+
if (res && res.length > 0) {
|
12
|
+
return res?.[1] || res?.[0];
|
13
|
+
}
|
14
|
+
return fileUrl;
|
15
|
+
}
|
16
|
+
|
17
|
+
/**
|
18
|
+
* 从 url 获取文件名称,包含后缀
|
19
|
+
* @param {*} fileName
|
20
|
+
* @param {*} str
|
21
|
+
* @returns
|
22
|
+
*/
|
23
|
+
export function getFullFileName(fileUrl) {
|
24
|
+
const res = fileUrl?.match(/[^\/]+?\.[^\/\.]+$/);
|
25
|
+
if (res && res.length > 0) {
|
26
|
+
return res[0];
|
27
|
+
}
|
28
|
+
return fileUrl;
|
29
|
+
}
|
30
|
+
|
31
|
+
/**
|
32
|
+
* 合并文件名称,自动处理后缀
|
33
|
+
* @param {*} fileName
|
34
|
+
* @param {*} str
|
35
|
+
* @returns
|
36
|
+
*/
|
37
|
+
export const mergeFileName = function (fileName, str) {
|
38
|
+
const name = fileName?.match(/([^\/]+?)\.[^\/]+$/)?.[1] || "";
|
39
|
+
const suffix = fileName?.match(/[^\/]+?\.([^\/]+)$/)?.[1] || "";
|
40
|
+
return `${name}${str}.${suffix}`;
|
41
|
+
};
|
42
|
+
|
43
|
+
/**
|
44
|
+
* 获取唯一的文件名
|
45
|
+
* @param {*} fileName
|
46
|
+
* @returns
|
47
|
+
*/
|
48
|
+
export const getUFileName = function (fileName) {
|
49
|
+
return mergeFileName(getFileName(fileName) || "rc-upload", `-${Date.now()}-${nanoid()}`);
|
50
|
+
};
|
51
|
+
|
52
|
+
/**
|
53
|
+
* 根据 file 对象获取文件名称,文件名称包含:初始名称、类型
|
54
|
+
* @param file
|
55
|
+
*/
|
56
|
+
export const getFileNameByFileObj = (file) => {
|
57
|
+
const _fileName = file.name || getFullFileName(file.url);
|
58
|
+
if (_fileName.indexOf("--Cs--") >= 0) {
|
59
|
+
return _fileName;
|
60
|
+
}
|
61
|
+
return mergeFileName(_fileName, `--Cs--${file.type?.replace(/[\/\-\+]/g, "_")}-${Date.now()}-${nanoid()}`);
|
62
|
+
};
|
@@ -0,0 +1,302 @@
|
|
1
|
+
import { getFileURL, getFileType, isBase64Str } from "./utils";
|
2
|
+
import { getUFileName, getFileName, getFullFileName } from "./fileName";
|
3
|
+
import { nanoid } from "./nanoid";
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 出入参归一化
|
7
|
+
* 统一成对象格式用于内部处理
|
8
|
+
{
|
9
|
+
}
|
10
|
+
|
11
|
+
*/
|
12
|
+
|
13
|
+
/**
|
14
|
+
* 出入参数据子项处理配置项
|
15
|
+
*/
|
16
|
+
export interface IFile {
|
17
|
+
/** 唯一 id */
|
18
|
+
uid: string;
|
19
|
+
/** 存储的文件名称 */
|
20
|
+
name: string;
|
21
|
+
/** 上传的文件名称 */
|
22
|
+
sourceName: string;
|
23
|
+
/** 文件地址 */
|
24
|
+
url: string;
|
25
|
+
/** 文件预览地址 */
|
26
|
+
previewUrl?: string;
|
27
|
+
/** 文件类型 */
|
28
|
+
type: string;
|
29
|
+
/** 文件大小 */
|
30
|
+
size: number;
|
31
|
+
/** 原始 file 文件 */
|
32
|
+
originFileObj?: File;
|
33
|
+
/** 最后更新时间戳 */
|
34
|
+
lastModified?: number;
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* 出入参数据处理配置项
|
39
|
+
*/
|
40
|
+
export interface IHandlerOpt {
|
41
|
+
/** 列表数据模式:数组 | 字符串 */
|
42
|
+
listMode: "array" | "jsonStr" | "splitStr";
|
43
|
+
/** 字符串列表数据模式下的分隔符 */
|
44
|
+
split: string;
|
45
|
+
/** 子项数据模式: 格式化对象 | file 文件格式 | url 地址 | JSON 序列化字符串 */
|
46
|
+
itemMode: "object" | "file" | "url" | "jsonStr";
|
47
|
+
/** 预览配置参数 */
|
48
|
+
previewConfig?: Object;
|
49
|
+
}
|
50
|
+
|
51
|
+
/** 内部文件格式 */
|
52
|
+
const fileTpl: IFile = {
|
53
|
+
uid: "",
|
54
|
+
/** 上传的文件名称 */
|
55
|
+
sourceName: "",
|
56
|
+
/** 存储的文件名称 */
|
57
|
+
name: "",
|
58
|
+
/** 文件地址 */
|
59
|
+
url: "",
|
60
|
+
/** 文件预览地址 */
|
61
|
+
previewUrl: "",
|
62
|
+
/** 文件类型 */
|
63
|
+
type: "",
|
64
|
+
/** 文件大小 */
|
65
|
+
size: undefined,
|
66
|
+
/** 最后更新时间戳 */
|
67
|
+
lastModified: undefined,
|
68
|
+
/** 源文件 */
|
69
|
+
originFileObj: undefined,
|
70
|
+
};
|
71
|
+
|
72
|
+
/**
|
73
|
+
* 对象字符串转为对象
|
74
|
+
* @param objStr
|
75
|
+
*/
|
76
|
+
export const objStrToObj = function (objStr) {
|
77
|
+
let _objStr = objStr;
|
78
|
+
if (typeof _objStr === "object") {
|
79
|
+
return _objStr;
|
80
|
+
}
|
81
|
+
// 对象字符串 转为 对象
|
82
|
+
if (typeof _objStr === "string") {
|
83
|
+
_objStr = _objStr.trim();
|
84
|
+
if (/^\[.*\]$|^\{.*\}$/.test(_objStr)) {
|
85
|
+
try {
|
86
|
+
_objStr = JSON.parse(_objStr);
|
87
|
+
} catch (error) {}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
return _objStr;
|
91
|
+
};
|
92
|
+
|
93
|
+
/**
|
94
|
+
* 预览 url 添加前缀
|
95
|
+
* @param {*} uri
|
96
|
+
* @param {*} previewConfig
|
97
|
+
* @returns
|
98
|
+
*/
|
99
|
+
export function mergePreviewConfig(uri, previewConfig) {
|
100
|
+
let _uri = uri?.trim();
|
101
|
+
// base64 直接返回
|
102
|
+
if (isBase64Str(_uri)) {
|
103
|
+
return _uri;
|
104
|
+
}
|
105
|
+
const isUriPattern = /http[s]?:\/\/[^\s]+/.test(_uri);
|
106
|
+
const query = previewConfig?.query?.trim() ? "?" + previewConfig?.query?.trim() : "";
|
107
|
+
|
108
|
+
if (isUriPattern) {
|
109
|
+
// 源地址为 http 地址,直接拼接 query 参数
|
110
|
+
return `${_uri}${query}`;
|
111
|
+
}
|
112
|
+
|
113
|
+
if (previewConfig) {
|
114
|
+
// 拼接前缀
|
115
|
+
return `${previewConfig?.url?.trim()}/${_uri}${query}`?.trim();
|
116
|
+
}
|
117
|
+
|
118
|
+
return _uri;
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* 子项数据归一化
|
123
|
+
* 字符串:url、base64
|
124
|
+
* 对象:
|
125
|
+
* 统一成对象格式用于内部处理
|
126
|
+
*/
|
127
|
+
export const handleInputItem = (data, opt: IHandlerOpt) => {
|
128
|
+
if (!data) {
|
129
|
+
console.warn("Upload handleInputData 请传入正确的数据");
|
130
|
+
return data;
|
131
|
+
}
|
132
|
+
let file = {
|
133
|
+
...fileTpl,
|
134
|
+
};
|
135
|
+
// 对象字符串 转为 对象
|
136
|
+
const _data = objStrToObj(data);
|
137
|
+
if (typeof _data === "string") {
|
138
|
+
file.url = _data;
|
139
|
+
file.sourceName = getFullFileName(file.url);
|
140
|
+
file.name = file.sourceName;
|
141
|
+
file.type = getFileType(_data);
|
142
|
+
} else if (typeof _data === "object") {
|
143
|
+
const sourceData = _data.originFileObj || _data;
|
144
|
+
file.uid = _data.uid;
|
145
|
+
file.url = sourceData.url || sourceData.ossUrl;
|
146
|
+
file.sourceName = _data.sourceName || getFullFileName(file.url) || _data.name;
|
147
|
+
file.name = _data.name || getFullFileName(file.url);
|
148
|
+
file.type = _data.type;
|
149
|
+
file.size = _data.size;
|
150
|
+
file.originFileObj = data instanceof File ? data : undefined;
|
151
|
+
file.lastModified = _data.lastModified;
|
152
|
+
}
|
153
|
+
if (!file.uid) {
|
154
|
+
file.uid = `${getFileName(file.url || file.name) || "rc-upload"}-${Date.now()}-${nanoid()}`;
|
155
|
+
}
|
156
|
+
if (!file.name) {
|
157
|
+
file.name = file.uid;
|
158
|
+
}
|
159
|
+
|
160
|
+
const { previewConfig } = opt || {};
|
161
|
+
|
162
|
+
// 合并预览地址?
|
163
|
+
file.previewUrl = mergePreviewConfig(file.url, previewConfig);
|
164
|
+
|
165
|
+
return file;
|
166
|
+
};
|
167
|
+
|
168
|
+
/**
|
169
|
+
* fileList 数据归一化
|
170
|
+
* 统一成对象格式用于内部处理
|
171
|
+
*/
|
172
|
+
export const handleInputFileList = (fileList, opt: IHandlerOpt) => {
|
173
|
+
if (!fileList) {
|
174
|
+
return [];
|
175
|
+
}
|
176
|
+
|
177
|
+
let _fileList = fileList;
|
178
|
+
const { listMode, split } = opt || {};
|
179
|
+
|
180
|
+
// 统一成数组格式
|
181
|
+
if (_fileList !== null && _fileList !== undefined && !Array.isArray(_fileList)) {
|
182
|
+
_fileList = [_fileList];
|
183
|
+
}
|
184
|
+
|
185
|
+
// splitStr jsonStr 模式下 Upload 自动将字符串嵌套了数组,需要手动处理
|
186
|
+
if (listMode === "splitStr" || listMode === "jsonStr") {
|
187
|
+
if (Array.isArray(_fileList) && _fileList.length === 1 && typeof _fileList[0] === "string") {
|
188
|
+
// @ts-ignore
|
189
|
+
_fileList = _fileList[0];
|
190
|
+
}
|
191
|
+
if (typeof _fileList === "string") {
|
192
|
+
if (listMode === "splitStr") {
|
193
|
+
// @ts-ignore
|
194
|
+
_fileList = _fileList?.split(split ?? ", ");
|
195
|
+
}
|
196
|
+
|
197
|
+
if (listMode === "jsonStr") {
|
198
|
+
_fileList = objStrToObj(_fileList);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
if (Array.isArray(_fileList)) {
|
204
|
+
const arrRes = [];
|
205
|
+
_fileList.forEach((it) => {
|
206
|
+
const itRes = handleInputItem(it, opt);
|
207
|
+
itRes && arrRes.push(itRes);
|
208
|
+
});
|
209
|
+
return arrRes;
|
210
|
+
}
|
211
|
+
return _fileList;
|
212
|
+
};
|
213
|
+
|
214
|
+
export const handleFileName = function () {};
|
215
|
+
|
216
|
+
/**
|
217
|
+
* 子项数据归一化
|
218
|
+
* @param data
|
219
|
+
* @returns
|
220
|
+
*/
|
221
|
+
export const formatItem = function (data) {
|
222
|
+
let url = data.url || (data instanceof File ? getFileURL(data) : data.url);
|
223
|
+
const res = {
|
224
|
+
/** 唯一 id */
|
225
|
+
uid: data.uid || getUFileName(data.name),
|
226
|
+
/** 上传的文件名称 */
|
227
|
+
sourceName: data.sourceName || data.name,
|
228
|
+
/** 存储的文件名称 */
|
229
|
+
name: data.url ? getFullFileName(data.url) : data.name,
|
230
|
+
/** 文件地址 */
|
231
|
+
url: url,
|
232
|
+
/** 文件类型 */
|
233
|
+
type: data.type,
|
234
|
+
/** 文件大小 */
|
235
|
+
size: data.size,
|
236
|
+
/** 最后更新时间戳 */
|
237
|
+
lastModified: data.lastModified,
|
238
|
+
originFileObj: data instanceof File ? data : undefined,
|
239
|
+
};
|
240
|
+
if (!res.name) {
|
241
|
+
res.name = res.uid;
|
242
|
+
}
|
243
|
+
if (!res.sourceName) {
|
244
|
+
res.sourceName = res.name;
|
245
|
+
}
|
246
|
+
return res;
|
247
|
+
};
|
248
|
+
|
249
|
+
/**
|
250
|
+
* 出参子项归一化
|
251
|
+
* 根据配置,将 对象 处理成所需格式
|
252
|
+
*/
|
253
|
+
export const handleUploadItem = function (file, opt: IHandlerOpt) {
|
254
|
+
const { itemMode = "object" } = opt || {};
|
255
|
+
if (itemMode === "file") {
|
256
|
+
return file?.originFileObj || file;
|
257
|
+
}
|
258
|
+
if (itemMode === "object") {
|
259
|
+
return formatItem(file);
|
260
|
+
}
|
261
|
+
if (itemMode === "jsonStr" && typeof file !== "string") {
|
262
|
+
const { originFileObj, ...jFile } = formatItem(file);
|
263
|
+
return JSON.stringify({ ...jFile });
|
264
|
+
}
|
265
|
+
if (itemMode === "url") {
|
266
|
+
return typeof file === "string" ? file : file.originFileObj?.url || file?.url;
|
267
|
+
}
|
268
|
+
return file;
|
269
|
+
};
|
270
|
+
|
271
|
+
/**
|
272
|
+
* 出参 fileList 归一化
|
273
|
+
* 根据配置,将 对象 处理成所需格式
|
274
|
+
*/
|
275
|
+
export const handleOutputFileList = function (fileList, opt: IHandlerOpt) {
|
276
|
+
if (!fileList) {
|
277
|
+
return undefined;
|
278
|
+
}
|
279
|
+
|
280
|
+
const { listMode, split } = opt || {};
|
281
|
+
|
282
|
+
let _fileList = fileList;
|
283
|
+
if (_fileList && !Array.isArray(_fileList)) {
|
284
|
+
_fileList = [fileList];
|
285
|
+
}
|
286
|
+
|
287
|
+
// 数组
|
288
|
+
if (listMode === "array") {
|
289
|
+
return _fileList?.map((it) => handleUploadItem(it, opt));
|
290
|
+
}
|
291
|
+
// 序列化后的字符串
|
292
|
+
if (listMode === "jsonStr") {
|
293
|
+
return JSON.stringify(_fileList?.map((it) => handleUploadItem(it, opt)));
|
294
|
+
}
|
295
|
+
// join 后的字符串
|
296
|
+
if (listMode === "splitStr") {
|
297
|
+
return _fileList
|
298
|
+
?.map((it) => handleUploadItem(it, opt))
|
299
|
+
.filter((it) => it)
|
300
|
+
.join(split ?? ", ");
|
301
|
+
}
|
302
|
+
};
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { axios } from "@hzab/data-model";
|
2
|
-
import {
|
2
|
+
import { getFileNameByFileObj } from "./fileName";
|
3
3
|
|
4
4
|
export function getSignature(opt = {}) {
|
5
5
|
const { serverUrl = "/api/v1/user/oss/getWebOssConfig" } = opt;
|
@@ -58,16 +58,7 @@ class OssUpload {
|
|
58
58
|
const formData = new FormData();
|
59
59
|
// key 表示上传到 Bucket 内的 Object 的完整路径,例如 exampledir/exampleobject.txtObject,完整路径中不能包含 Bucket 名称。
|
60
60
|
// filename 表示待上传的本地文件名称。
|
61
|
-
|
62
|
-
if (file?.name) {
|
63
|
-
const nameArr = file?.name.match(/^(.+)\.(.+)$/);
|
64
|
-
if (nameArr && nameArr.length > 2) {
|
65
|
-
filename = `${nameArr[1]}_${Date.now()}_${nanoid()}.${nameArr[2]}`;
|
66
|
-
}
|
67
|
-
}
|
68
|
-
if (!filename) {
|
69
|
-
filename = `${Date.now()}_${nanoid()}.${file.type?.replace(/\w+\/, ''/)}`;
|
70
|
-
}
|
61
|
+
const filename = getFileNameByFileObj(file);
|
71
62
|
const key = `${ossParams?.dir}${filename}`;
|
72
63
|
formData.set("key", key);
|
73
64
|
formData.set("OSSAccessKeyId", ossParams.accessid);
|
@@ -122,7 +113,7 @@ export async function handleOssUpload(files, opt) {
|
|
122
113
|
const promise = [];
|
123
114
|
_files?.forEach((file) => {
|
124
115
|
// 数据已经是 url 的情况
|
125
|
-
if (typeof file === "string" || file.ossUrl) {
|
116
|
+
if (typeof file === "string" || file.ossUrl || file.url) {
|
126
117
|
promise.push(Promise.resolve(file));
|
127
118
|
} else {
|
128
119
|
promise.push(
|
@@ -144,7 +135,12 @@ export async function handleOssUpload(files, opt) {
|
|
144
135
|
|
145
136
|
return Promise.all(promise).then((filePromises) => {
|
146
137
|
filePromises?.forEach((fileUrl, idx) => {
|
147
|
-
|
138
|
+
if (typeof fileUrl === "string") {
|
139
|
+
_files[idx].ossUrl = fileUrl;
|
140
|
+
_files[idx].url = fileUrl;
|
141
|
+
} else if (_files[idx].url) {
|
142
|
+
_files[idx].ossUrl = _files[idx].url;
|
143
|
+
}
|
148
144
|
});
|
149
145
|
return Promise.resolve(_files);
|
150
146
|
});
|
@@ -7,19 +7,24 @@ import { nanoid } from "nanoid";
|
|
7
7
|
* blob:http://localhost:8000/c9950644-5118-4231-9be7-8183bde1fdc7
|
8
8
|
*/
|
9
9
|
export function getFileURL(file) {
|
10
|
-
|
10
|
+
const _file = file?.file || file;
|
11
|
+
if (!(_file instanceof File)) {
|
12
|
+
return;
|
13
|
+
}
|
14
|
+
|
15
|
+
let url = _file.url || null;
|
11
16
|
|
12
17
|
try {
|
13
18
|
// 下面函数执行的效果是一样的,只是需要针对不同的浏览器执行不同的 js 函数而已
|
14
19
|
if (window.createObjectURL != undefined) {
|
15
20
|
// basic
|
16
|
-
|
21
|
+
return window.createObjectURL(_file);
|
17
22
|
} else if (window.URL != undefined) {
|
18
23
|
// mozilla(firefox)
|
19
|
-
|
24
|
+
return window.URL.createObjectURL(_file);
|
20
25
|
} else if (window.webkitURL != undefined) {
|
21
26
|
// webkit or chrome
|
22
|
-
|
27
|
+
return window.webkitURL.createObjectURL(_file);
|
23
28
|
}
|
24
29
|
} catch (error) {
|
25
30
|
console.warn("getFileURL Error: ", error);
|
@@ -28,12 +33,25 @@ export function getFileURL(file) {
|
|
28
33
|
return url;
|
29
34
|
}
|
30
35
|
|
36
|
+
/**
|
37
|
+
* 是否是 base64 字符串
|
38
|
+
* @param {*} str
|
39
|
+
* @returns
|
40
|
+
*/
|
41
|
+
export const isBase64Str = function (str) {
|
42
|
+
return /^data:\w+\/\w+;base64/i.test(str);
|
43
|
+
};
|
44
|
+
|
31
45
|
/**
|
32
46
|
* 判断 url 是否带有指定图片后缀
|
33
47
|
* @param {string} url
|
34
48
|
* @returns
|
35
49
|
*/
|
36
50
|
export function checkImageUrl(url) {
|
51
|
+
// base64
|
52
|
+
if (/^data:image\/\w+;base64/i.test(url)) {
|
53
|
+
return true;
|
54
|
+
}
|
37
55
|
const imgTypes = [
|
38
56
|
"apng",
|
39
57
|
"avif",
|
@@ -62,7 +80,7 @@ export function checkImageUrl(url) {
|
|
62
80
|
*/
|
63
81
|
export function isValidMediaType(input) {
|
64
82
|
const validPrefixes = ["image/", "video/", "audio/"];
|
65
|
-
return validPrefixes.some((prefix) => input
|
83
|
+
return validPrefixes.some((prefix) => input?.startsWith(prefix));
|
66
84
|
}
|
67
85
|
|
68
86
|
/**
|
@@ -102,14 +120,6 @@ export function checkUrlSuffix(url, types = [], caseSensitive) {
|
|
102
120
|
}
|
103
121
|
}
|
104
122
|
|
105
|
-
export function getFileName(fileUrl) {
|
106
|
-
const res = fileUrl?.match(/[^\/]+?\.[^\/]+$/);
|
107
|
-
if (res && res.length > 0) {
|
108
|
-
return res[0];
|
109
|
-
}
|
110
|
-
return fileUrl;
|
111
|
-
}
|
112
|
-
|
113
123
|
export function getFileType(file) {
|
114
124
|
if (typeof file === "object" && file?.type) {
|
115
125
|
return file?.type;
|
@@ -149,6 +159,12 @@ export function handleMaxCount(fileList, maxCount) {
|
|
149
159
|
return list;
|
150
160
|
}
|
151
161
|
|
162
|
+
/**
|
163
|
+
* 预览 url 添加前缀
|
164
|
+
* @param {*} uri
|
165
|
+
* @param {*} previewConfig
|
166
|
+
* @returns
|
167
|
+
*/
|
152
168
|
export function mergePreviewConfig(uri, previewConfig) {
|
153
169
|
const isUriPattern = /http[s]?:\/\/[^\s]+/.test(uri);
|
154
170
|
const query = previewConfig?.query ? "?" + previewConfig?.query : "";
|
@@ -15,9 +15,8 @@ export const ItemList = (props) => {
|
|
15
15
|
if (!it) {
|
16
16
|
return null;
|
17
17
|
}
|
18
|
-
const { name,
|
19
|
-
|
20
|
-
let src = url || getFileURL(it);
|
18
|
+
const { name, previewUrl = it?.url || it?.ossUrl } = it || {};
|
19
|
+
let src = previewUrl || getFileURL(it?.originFileObj || it);
|
21
20
|
|
22
21
|
if (baseUrl) {
|
23
22
|
src = baseUrl + src;
|