@hzab/form-render-mobile 0.0.1
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/README.md +67 -0
- package/lib/index.js +46475 -0
- package/lib/static/imgs/approachPointIcon_f2eb69df51.png +0 -0
- package/lib/static/imgs/layers-2x_8f2c4d1147.png +0 -0
- package/lib/static/imgs/layers_416d91365b.png +0 -0
- package/lib/static/imgs/marker-icon_2b3e1faf89.png +0 -0
- package/package.json +59 -0
- package/src/common/schemaHandler.js +55 -0
- package/src/components/Cascader/index.jsx +75 -0
- package/src/components/Cascader/index.less +3 -0
- package/src/components/DatePicker/index.less +3 -0
- package/src/components/DatePicker/index.tsx +98 -0
- package/src/components/MapPicker/common/approachPointIcon.png +0 -0
- package/src/components/MapPicker/common/getCurrentPosition.js +27 -0
- package/src/components/MapPicker/common/gpsConvert.js +236 -0
- package/src/components/MapPicker/common/map-config.js +7 -0
- package/src/components/MapPicker/index.jsx +82 -0
- package/src/components/MapPicker/line-picker/car-route.json +426 -0
- package/src/components/MapPicker/line-picker/index.jsx +188 -0
- package/src/components/MapPicker/line-picker/index.less +41 -0
- package/src/components/MapPicker/line-picker/mockLonLat.js +1 -0
- package/src/components/MapPicker/location-picker/index.jsx +104 -0
- package/src/components/MapPicker/location-picker/index.less +36 -0
- package/src/components/NumberPicker/index.jsx +16 -0
- package/src/components/NumberPicker/index.less +3 -0
- package/src/components/Password/index.jsx +14 -0
- package/src/components/Select/index.less +3 -0
- package/src/components/Select/index.tsx +60 -0
- package/src/components/Text/index.tsx +23 -0
- package/src/components/Uploader/common/cordova-camera.js +188 -0
- package/src/components/Uploader/common/utils.js +87 -0
- package/src/components/Uploader/index.jsx +31 -0
- package/src/components/Uploader/uploader.jsx +246 -0
- package/src/components/Uploader/uploader.less +36 -0
- package/src/components/Uploader/video/index.jsx +37 -0
- package/src/components/Uploader/video/index.less +37 -0
- package/src/components/index.ts +10 -0
- package/src/index.tsx +101 -0
- package/src/type.d.ts +15 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Marker, TileLayer, Map } from "react-leaflet";
|
|
3
|
+
import "leaflet/dist/leaflet.css";
|
|
4
|
+
import { Button, SpinLoading } from "antd-mobile";
|
|
5
|
+
|
|
6
|
+
import { wgs84togcj02 } from "../common/gpsConvert";
|
|
7
|
+
|
|
8
|
+
import getCurrentPosition from "../common/getCurrentPosition";
|
|
9
|
+
import { mapConfig } from "../common/map-config";
|
|
10
|
+
import pointIconUrl from "../common/approachPointIcon.png";
|
|
11
|
+
|
|
12
|
+
import "./index.less";
|
|
13
|
+
|
|
14
|
+
// 地图选中点图标
|
|
15
|
+
const LeafIcon = new L.Icon({
|
|
16
|
+
iconUrl: pointIconUrl,
|
|
17
|
+
iconSize: [30, 40],
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
let map = null;
|
|
21
|
+
|
|
22
|
+
function ReactLeafletMap(props) {
|
|
23
|
+
const {
|
|
24
|
+
onConfirm,
|
|
25
|
+
onCancel,
|
|
26
|
+
data,
|
|
27
|
+
// TODO: 切换成正确的地址(通过 props | config 传递?)
|
|
28
|
+
mapUrl = "https://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
|
|
29
|
+
// mapUrl = "http://20.0.6.168:8083/api/map/org/{z}/{y}/{x}",
|
|
30
|
+
} = props;
|
|
31
|
+
const [point, setPoint] = useState(null);
|
|
32
|
+
const [loading, setLoading] = useState(false);
|
|
33
|
+
const mapRef = useRef();
|
|
34
|
+
|
|
35
|
+
function onClickMap(e) {
|
|
36
|
+
setPoint(e?.latlng);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getPosition() {
|
|
40
|
+
setLoading(true);
|
|
41
|
+
getCurrentPosition()
|
|
42
|
+
.then((position) => {
|
|
43
|
+
const { coords } = position;
|
|
44
|
+
const [lat, lng] = wgs84togcj02(coords.longitude, coords.latitude, true);
|
|
45
|
+
setPoint({ lat, lng });
|
|
46
|
+
map.setView(L.latLng(lat, lng), mapConfig.zoom);
|
|
47
|
+
setLoading(false);
|
|
48
|
+
})
|
|
49
|
+
.catch((err) => {
|
|
50
|
+
console.error("getPosition", err);
|
|
51
|
+
setLoading(false);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
map = mapRef?.current?.contextValue?.map;
|
|
57
|
+
|
|
58
|
+
// 数据回填
|
|
59
|
+
if (data && data.lng && data.lat) {
|
|
60
|
+
const { lat, lng } = data;
|
|
61
|
+
map.setView(L.latLng(lat, lng), mapConfig.zoom);
|
|
62
|
+
setPoint({ lat, lng });
|
|
63
|
+
} else {
|
|
64
|
+
// 获取初始位置(当前位置)
|
|
65
|
+
getPosition();
|
|
66
|
+
}
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
function onSave() {
|
|
70
|
+
onConfirm && onConfirm(point);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div className="location-picker">
|
|
75
|
+
<div className="location-picker-header">
|
|
76
|
+
<span>经度:{point?.lng}</span>
|
|
77
|
+
|
|
78
|
+
<span>纬度:{point?.lat}</span>
|
|
79
|
+
<span className="get-current-location-btn" onClick={getPosition}>
|
|
80
|
+
获取当前坐标
|
|
81
|
+
</span>
|
|
82
|
+
</div>
|
|
83
|
+
<Map className="location-picker-map" {...mapConfig} ref={mapRef} onclick={onClickMap}>
|
|
84
|
+
<TileLayer url={mapUrl} />
|
|
85
|
+
{point && <Marker key={`marker`} position={point} icon={LeafIcon} />}
|
|
86
|
+
</Map>
|
|
87
|
+
<div className="location-picker-footer">
|
|
88
|
+
<Button className="footer-btn" onClick={onCancel}>
|
|
89
|
+
取消
|
|
90
|
+
</Button>
|
|
91
|
+
<Button className="footer-btn" color="primary" fill="solid" onClick={onSave}>
|
|
92
|
+
确定
|
|
93
|
+
</Button>
|
|
94
|
+
</div>
|
|
95
|
+
{loading ? (
|
|
96
|
+
<div className="loading-wrap">
|
|
97
|
+
<SpinLoading />
|
|
98
|
+
</div>
|
|
99
|
+
) : null}
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default ReactLeafletMap;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.location-picker {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
height: 100%;
|
|
6
|
+
.location-picker-header {
|
|
7
|
+
display: flex;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
padding: 0 20px;
|
|
10
|
+
.get-current-location-btn {
|
|
11
|
+
padding: 0 8px;
|
|
12
|
+
cursor: pointer;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
.location-picker-map {
|
|
16
|
+
flex: 1;
|
|
17
|
+
}
|
|
18
|
+
.location-picker-footer {
|
|
19
|
+
display: flex;
|
|
20
|
+
.footer-btn {
|
|
21
|
+
flex: 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
.loading-wrap {
|
|
25
|
+
position: absolute;
|
|
26
|
+
top: 0;
|
|
27
|
+
right: 0;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
left: 0;
|
|
30
|
+
display: flex;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
align-items: center;
|
|
33
|
+
background: rgba(0, 0, 0, 0.55);
|
|
34
|
+
z-index: 1000;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { connect, mapProps } from "@formily/react";
|
|
3
|
+
import { Stepper } from "antd-mobile";
|
|
4
|
+
|
|
5
|
+
import "./index.less";
|
|
6
|
+
|
|
7
|
+
function NumberPicker({ precision, className, ...props }) {
|
|
8
|
+
return <Stepper className={`number-picker ${className}`} {...props} digits={precision} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default connect(
|
|
12
|
+
NumberPicker,
|
|
13
|
+
mapProps((props, field) => {
|
|
14
|
+
return { ...props, form: field.form, field };
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { connect, mapProps } from "@formily/react";
|
|
3
|
+
import { Input } from "antd-mobile";
|
|
4
|
+
|
|
5
|
+
function Password(props) {
|
|
6
|
+
return <Input {...props} type="password" />;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default connect(
|
|
10
|
+
Password,
|
|
11
|
+
mapProps((props, field) => {
|
|
12
|
+
return { ...props, form: field.form, field };
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { connect, mapProps } from "@formily/react";
|
|
3
|
+
import { Picker } from "antd-mobile";
|
|
4
|
+
|
|
5
|
+
import "./index.less";
|
|
6
|
+
|
|
7
|
+
function Select(props) {
|
|
8
|
+
const { disabled, readOnly, placeholder = "请选择", field = {}, onChange } = props;
|
|
9
|
+
|
|
10
|
+
const { dataSource } = field;
|
|
11
|
+
|
|
12
|
+
const [_options, setOptions] = useState(dataSource ? [dataSource] : []);
|
|
13
|
+
|
|
14
|
+
const [visible, setVisible] = useState(false);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (dataSource) {
|
|
18
|
+
setOptions([dataSource]);
|
|
19
|
+
}
|
|
20
|
+
}, [dataSource]);
|
|
21
|
+
|
|
22
|
+
function onClose() {
|
|
23
|
+
setVisible(false);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function onClick() {
|
|
27
|
+
if (readOnly || disabled) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
setVisible((val) => {
|
|
31
|
+
return !val;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function onConfirm(value) {
|
|
36
|
+
onChange && onChange(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="formily-select" onClick={onClick}>
|
|
41
|
+
<Picker
|
|
42
|
+
{...props}
|
|
43
|
+
style={{ pointerEvents: readOnly || disabled ? "none" : undefined }}
|
|
44
|
+
columns={_options}
|
|
45
|
+
visible={visible}
|
|
46
|
+
onClose={onClose}
|
|
47
|
+
onConfirm={onConfirm}
|
|
48
|
+
>
|
|
49
|
+
{(value) => (value && value.length > 0 ? value[0]?.label : placeholder)}
|
|
50
|
+
</Picker>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default connect(
|
|
56
|
+
Select,
|
|
57
|
+
mapProps((props, field) => {
|
|
58
|
+
return { ...props, form: field.form, field };
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { connect, mapProps } from "@formily/react";
|
|
3
|
+
|
|
4
|
+
function Text({ value, mode, content, className, ...props }) {
|
|
5
|
+
const tagName = mode === "normal" || !mode ? "div" : mode;
|
|
6
|
+
console.log("props", props.readOnly);
|
|
7
|
+
|
|
8
|
+
return React.createElement(
|
|
9
|
+
tagName,
|
|
10
|
+
{
|
|
11
|
+
className: `formily-text ${className}`,
|
|
12
|
+
...props,
|
|
13
|
+
},
|
|
14
|
+
value || content,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default connect(
|
|
19
|
+
Text,
|
|
20
|
+
mapProps((props, field) => {
|
|
21
|
+
return { ...props, form: field.form, field };
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export function getImage(_options = {}) {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
// TODO: 调整 options 参数
|
|
4
|
+
const options = {
|
|
5
|
+
quality: 0,
|
|
6
|
+
destinationType: 1, // 0-Return base64 encoded string. 1-Return file uri (content://media/external/images/media/2 for Android)
|
|
7
|
+
sourceType: 1, // 0-Photo Library, 1-Camera, 2-Saved Album Choose image only from the device's Camera Roll album
|
|
8
|
+
encodingType: 0, // 0=JPG 1=PNG
|
|
9
|
+
..._options,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
navigator.device.capture.captureImage(
|
|
13
|
+
function (mediaFiles) {
|
|
14
|
+
const imgData = mediaFiles[0].localURL;
|
|
15
|
+
let reader;
|
|
16
|
+
let imgBlob;
|
|
17
|
+
window.resolveLocalFileSystemURL(
|
|
18
|
+
imgData,
|
|
19
|
+
function (fileEntry) {
|
|
20
|
+
fileEntry.file(
|
|
21
|
+
function (file) {
|
|
22
|
+
reader = new FileReader();
|
|
23
|
+
reader.onloadend = function (e) {
|
|
24
|
+
imgBlob = new Blob([this.result], { type: "image/jpeg" });
|
|
25
|
+
console.log("imgBlob", imgBlob);
|
|
26
|
+
if (!imgBlob?.name) {
|
|
27
|
+
imgBlob.name = `${Date.now()}.${imgBlob.type?.replace("image/", "")}`;
|
|
28
|
+
}
|
|
29
|
+
if (!imgBlob?.lastModifiedDate) {
|
|
30
|
+
imgBlob.lastModifiedDate = new Date();
|
|
31
|
+
}
|
|
32
|
+
if (!imgBlob?.lastModified) {
|
|
33
|
+
imgBlob.lastModified = imgBlob.lastModifiedDate.getTime();
|
|
34
|
+
}
|
|
35
|
+
if (!imgBlob?.localURL) {
|
|
36
|
+
imgBlob.localURL = mediaFiles[0].localURL;
|
|
37
|
+
}
|
|
38
|
+
resolve(imgBlob);
|
|
39
|
+
// window.__file = imgBlob; // PLACE THE FILE ASSIGNMENT HERE AFTER THE READER HAS INGESTED THE FILE BYTES
|
|
40
|
+
};
|
|
41
|
+
reader.readAsArrayBuffer(file);
|
|
42
|
+
},
|
|
43
|
+
function (err) {
|
|
44
|
+
reject(err);
|
|
45
|
+
console.log("error with photo file");
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
function (err) {
|
|
50
|
+
reject(err);
|
|
51
|
+
console.log("error with photo file");
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
function (err) {
|
|
56
|
+
reject(err);
|
|
57
|
+
alert("Error taking picture", "Error");
|
|
58
|
+
},
|
|
59
|
+
options,
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getVideo(_options = {}) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
// TODO: 调整 options 参数
|
|
67
|
+
const options = {
|
|
68
|
+
quality: 0,
|
|
69
|
+
destinationType: 1,
|
|
70
|
+
sourceType: 1, // 0:Photo Library, 1=Camera, 2=Saved Album
|
|
71
|
+
encodingType: 0, // 0=JPG 1=PNG
|
|
72
|
+
..._options,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
navigator.device.capture.captureVideo(
|
|
76
|
+
function (mediaFiles) {
|
|
77
|
+
const imgData = mediaFiles[0].localURL;
|
|
78
|
+
let reader;
|
|
79
|
+
let imgBlob;
|
|
80
|
+
window.resolveLocalFileSystemURL(
|
|
81
|
+
imgData,
|
|
82
|
+
function (fileEntry) {
|
|
83
|
+
fileEntry.file(
|
|
84
|
+
function (file) {
|
|
85
|
+
reader = new FileReader();
|
|
86
|
+
reader.onloadend = function (e) {
|
|
87
|
+
imgBlob = new Blob([this.result], { type: "video/mp4" });
|
|
88
|
+
console.log("imgBlob", imgBlob);
|
|
89
|
+
if (!imgBlob?.name) {
|
|
90
|
+
imgBlob.name = `${Date.now()}.${imgBlob.type?.replace("video/", "")}`;
|
|
91
|
+
}
|
|
92
|
+
if (!imgBlob?.lastModifiedDate) {
|
|
93
|
+
imgBlob.lastModifiedDate = new Date();
|
|
94
|
+
}
|
|
95
|
+
if (!imgBlob?.lastModified) {
|
|
96
|
+
imgBlob.lastModified = imgBlob.lastModifiedDate.getTime();
|
|
97
|
+
}
|
|
98
|
+
if (!imgBlob?.localURL) {
|
|
99
|
+
imgBlob.localURL = mediaFiles[0].localURL;
|
|
100
|
+
}
|
|
101
|
+
resolve(imgBlob);
|
|
102
|
+
// window.__file = imgBlob; // PLACE THE FILE ASSIGNMENT HERE AFTER THE READER HAS INGESTED THE FILE BYTES
|
|
103
|
+
};
|
|
104
|
+
reader.readAsArrayBuffer(file);
|
|
105
|
+
},
|
|
106
|
+
function (err) {
|
|
107
|
+
reject(err);
|
|
108
|
+
console.log("error with photo file");
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
},
|
|
112
|
+
function (err) {
|
|
113
|
+
reject(err);
|
|
114
|
+
console.log("error with photo file");
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
function (err) {
|
|
119
|
+
reject(err);
|
|
120
|
+
alert("Error taking picture", "Error");
|
|
121
|
+
},
|
|
122
|
+
options,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function getAudio(_options = {}) {
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
// TODO: 调整 options 参数
|
|
130
|
+
const options = {
|
|
131
|
+
..._options,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
navigator.device.capture.captureAudio(
|
|
135
|
+
function (mediaFiles) {
|
|
136
|
+
const imgData = mediaFiles[0].localURL;
|
|
137
|
+
let reader;
|
|
138
|
+
let audioBlob;
|
|
139
|
+
window.resolveLocalFileSystemURL(
|
|
140
|
+
imgData,
|
|
141
|
+
function (fileEntry) {
|
|
142
|
+
fileEntry.file(
|
|
143
|
+
function (file) {
|
|
144
|
+
// TODO: 文件无法正常读取
|
|
145
|
+
reader = new FileReader();
|
|
146
|
+
reader.onloadend = function (e) {
|
|
147
|
+
audioBlob = new Blob([this.result], { type: "audio/mp3" });
|
|
148
|
+
console.log("audioBlob", audioBlob);
|
|
149
|
+
if (!audioBlob?.name) {
|
|
150
|
+
audioBlob.name = `${Date.now()}.${audioBlob.type?.replace("audio/", "")}`;
|
|
151
|
+
}
|
|
152
|
+
if (!audioBlob?.lastModifiedDate) {
|
|
153
|
+
audioBlob.lastModifiedDate = new Date();
|
|
154
|
+
}
|
|
155
|
+
if (!audioBlob?.lastModified) {
|
|
156
|
+
audioBlob.lastModified = audioBlob.lastModifiedDate.getTime();
|
|
157
|
+
}
|
|
158
|
+
if (!audioBlob?.localURL) {
|
|
159
|
+
audioBlob.localURL = mediaFiles[0].localURL;
|
|
160
|
+
}
|
|
161
|
+
resolve(audioBlob);
|
|
162
|
+
// window.__file = audioBlob; // PLACE THE FILE ASSIGNMENT HERE AFTER THE READER HAS INGESTED THE FILE BYTES
|
|
163
|
+
};
|
|
164
|
+
reader.onerror = function (e) {
|
|
165
|
+
console.log("reader.onerror: ", e);
|
|
166
|
+
};
|
|
167
|
+
reader.readAsArrayBuffer(file);
|
|
168
|
+
},
|
|
169
|
+
function (err) {
|
|
170
|
+
reject(err);
|
|
171
|
+
console.log("error with audio file");
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
function (err) {
|
|
176
|
+
reject(err);
|
|
177
|
+
console.log("error with audio file");
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
},
|
|
181
|
+
function (err) {
|
|
182
|
+
reject(err);
|
|
183
|
+
alert("Error taking audio", "Error");
|
|
184
|
+
},
|
|
185
|
+
options,
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 建立一个可以存取该 file 的 url
|
|
3
|
+
* @param {Object} file 文件
|
|
4
|
+
* @returns {string} url
|
|
5
|
+
* blob:http://localhost:8000/c9950644-5118-4231-9be7-8183bde1fdc7
|
|
6
|
+
*/
|
|
7
|
+
export function getFileURL(file) {
|
|
8
|
+
let url = null;
|
|
9
|
+
|
|
10
|
+
// 下面函数执行的效果是一样的,只是需要针对不同的浏览器执行不同的 js 函数而已
|
|
11
|
+
if (window.createObjectURL != undefined) {
|
|
12
|
+
// basic
|
|
13
|
+
url = window.createObjectURL(file);
|
|
14
|
+
} else if (window.URL != undefined) {
|
|
15
|
+
// mozilla(firefox)
|
|
16
|
+
url = window.URL.createObjectURL(file);
|
|
17
|
+
} else if (window.webkitURL != undefined) {
|
|
18
|
+
// webkit or chrome
|
|
19
|
+
url = window.webkitURL.createObjectURL(file);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return url;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 判断 url 是否带有指定图片后缀
|
|
27
|
+
* @param {string} url
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
export function checkImageUrl(url) {
|
|
31
|
+
const imgTypes = [
|
|
32
|
+
"apng",
|
|
33
|
+
"avif",
|
|
34
|
+
"bmp",
|
|
35
|
+
"gif",
|
|
36
|
+
"ico",
|
|
37
|
+
"cur",
|
|
38
|
+
"jpg",
|
|
39
|
+
"jpeg",
|
|
40
|
+
"jfif",
|
|
41
|
+
"pjpeg",
|
|
42
|
+
"pjp",
|
|
43
|
+
"png",
|
|
44
|
+
"svg",
|
|
45
|
+
"tif",
|
|
46
|
+
"tiff",
|
|
47
|
+
"webp",
|
|
48
|
+
];
|
|
49
|
+
return checkUrlSuffix(url, imgTypes);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 判断 url 是否带有指定视频后缀
|
|
54
|
+
* @param {string} url
|
|
55
|
+
* @returns
|
|
56
|
+
*/
|
|
57
|
+
export function checkVideoUrl(url) {
|
|
58
|
+
const imgTypes = ["3gp", "mpg", "mpeg", "mp4", "m4v", "m4p", "ogv", "ogg", "mov", "webm"];
|
|
59
|
+
return checkUrlSuffix(url, imgTypes);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 判断 url 是否带有指定音频后缀
|
|
64
|
+
* @param {string} url
|
|
65
|
+
* @returns
|
|
66
|
+
*/
|
|
67
|
+
export function checkAudioUrl(url) {
|
|
68
|
+
const imgTypes = ["3gp", "adts", "mpeg", "mp3", "mp4", "ogg", "mov", "webm", "rtp", "amr", "wav"];
|
|
69
|
+
return checkUrlSuffix(url, imgTypes);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 检查 url 是否带有指定后缀
|
|
74
|
+
* @param {string} url url 地址
|
|
75
|
+
* @param {Array} types 后缀数组
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
78
|
+
export function checkUrlSuffix(url, types = [], caseSensitive) {
|
|
79
|
+
if (!url) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
let _url = url?.replace(/\?.+/, "");
|
|
83
|
+
const reg = new RegExp(`\.(${types.join("|")})$`, caseSensitive ? undefined : "i");
|
|
84
|
+
if (reg.test(_url)) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Form } from "antd-mobile";
|
|
2
|
+
import { connect, mapProps } from "@formily/react";
|
|
3
|
+
|
|
4
|
+
import Uploader from "./uploader";
|
|
5
|
+
|
|
6
|
+
function UploaderCom(props) {
|
|
7
|
+
const { field = {}, form, onChange } = props;
|
|
8
|
+
const { name, mode } = field;
|
|
9
|
+
|
|
10
|
+
function onUploadChange(files) {
|
|
11
|
+
if (field?.autoUpload && props.fieldsConf[name]?.onUpload) {
|
|
12
|
+
props.fieldsConf[name]?.onUpload(files);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
onChange && onChange(files);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const _props = {
|
|
19
|
+
mode: mode,
|
|
20
|
+
...props,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return <Uploader {..._props} onChange={onUploadChange} />;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default connect(
|
|
27
|
+
UploaderCom,
|
|
28
|
+
mapProps((props, field) => {
|
|
29
|
+
return { ...props, form: field.form, field };
|
|
30
|
+
}),
|
|
31
|
+
);
|