@ant-design/agentic-ui 2.9.0 → 2.10.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/dist/ChatLayout/style.js +4 -1
- package/dist/Hooks/useLanguage.d.ts +3 -0
- package/dist/I18n/locales.d.ts +3 -0
- package/dist/I18n/locales.js +6 -0
- package/dist/MarkdownInputField/AttachmentButton/AttachmentButtonPopover.d.ts +4 -0
- package/dist/MarkdownInputField/AttachmentButton/AttachmentButtonPopover.js +198 -10
- package/dist/MarkdownInputField/AttachmentButton/index.js +17 -2
- package/dist/MarkdownInputField/AttachmentButton/utils.d.ts +60 -0
- package/dist/MarkdownInputField/AttachmentButton/utils.js +302 -0
- package/dist/MarkdownInputField/FileUploadManager/index.js +46 -2
- package/package.json +1 -1
package/dist/ChatLayout/style.js
CHANGED
|
@@ -166,7 +166,10 @@ var genStyle = function(token) {
|
|
|
166
166
|
flex: 1,
|
|
167
167
|
overflowY: 'auto',
|
|
168
168
|
overflowX: 'hidden',
|
|
169
|
-
|
|
169
|
+
paddingTop: 'var(--padding-2x)',
|
|
170
|
+
paddingLeft: 'var(--padding-2x)',
|
|
171
|
+
paddingRight: 'var(--padding-2x)',
|
|
172
|
+
paddingBottom: '144px',
|
|
170
173
|
'&::-webkit-scrollbar': {
|
|
171
174
|
width: '6px'
|
|
172
175
|
},
|
|
@@ -347,6 +347,9 @@ export declare function useLanguage(): {
|
|
|
347
347
|
'input.voiceInput': string;
|
|
348
348
|
'input.voiceInputting': string;
|
|
349
349
|
'input.placeholder': string;
|
|
350
|
+
'input.selectFile': string;
|
|
351
|
+
'input.openGallery': string;
|
|
352
|
+
'input.openFile': string;
|
|
350
353
|
'common.name': string;
|
|
351
354
|
'common.updateTime': string;
|
|
352
355
|
'common.type': string;
|
package/dist/I18n/locales.d.ts
CHANGED
|
@@ -311,6 +311,9 @@ export declare const cnLabels: {
|
|
|
311
311
|
'input.voiceInput': string;
|
|
312
312
|
'input.voiceInputting': string;
|
|
313
313
|
'input.placeholder': string;
|
|
314
|
+
'input.selectFile': string;
|
|
315
|
+
'input.openGallery': string;
|
|
316
|
+
'input.openFile': string;
|
|
314
317
|
'common.name': string;
|
|
315
318
|
'common.updateTime': string;
|
|
316
319
|
'common.type': string;
|
package/dist/I18n/locales.js
CHANGED
|
@@ -330,6 +330,9 @@
|
|
|
330
330
|
'input.voiceInput': '语音输入',
|
|
331
331
|
'input.voiceInputting': '语音输入中,点击可停止。',
|
|
332
332
|
'input.placeholder': '请输入',
|
|
333
|
+
'input.selectFile': '选择文件',
|
|
334
|
+
'input.openGallery': '打开相册',
|
|
335
|
+
'input.openFile': '打开文件',
|
|
333
336
|
// Other translations
|
|
334
337
|
'common.name': '名称',
|
|
335
338
|
'common.updateTime': '更新时间',
|
|
@@ -689,6 +692,9 @@
|
|
|
689
692
|
'input.voiceInput': 'Voice input',
|
|
690
693
|
'input.voiceInputting': 'Voice input in progress, click to stop.',
|
|
691
694
|
'input.placeholder': 'Please input',
|
|
695
|
+
'input.selectFile': 'Select File',
|
|
696
|
+
'input.openGallery': 'Open Gallery',
|
|
697
|
+
'input.openFile': 'Open File',
|
|
692
698
|
// Other translations
|
|
693
699
|
'common.name': 'Name',
|
|
694
700
|
'common.updateTime': 'Update Time',
|
|
@@ -9,6 +9,10 @@ export type SupportedFormat = {
|
|
|
9
9
|
export type AttachmentButtonPopoverProps = {
|
|
10
10
|
children?: React.ReactNode;
|
|
11
11
|
supportedFormat?: SupportedFormat;
|
|
12
|
+
/** 文件选择后的回调函数,参数为选中的文件 */
|
|
13
|
+
onFileSelect?: (files: FileList, accept: string) => void;
|
|
14
|
+
/** 是否允许一次选择多个文件 */
|
|
15
|
+
allowMultiple?: boolean;
|
|
12
16
|
};
|
|
13
17
|
export declare const SupportedFileFormats: {
|
|
14
18
|
image: {
|
|
@@ -1,7 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
function _array_like_to_array(arr, len) {
|
|
2
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
3
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
4
|
+
return arr2;
|
|
5
|
+
}
|
|
6
|
+
function _array_with_holes(arr) {
|
|
7
|
+
if (Array.isArray(arr)) return arr;
|
|
8
|
+
}
|
|
9
|
+
function _iterable_to_array_limit(arr, i) {
|
|
10
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
11
|
+
if (_i == null) return;
|
|
12
|
+
var _arr = [];
|
|
13
|
+
var _n = true;
|
|
14
|
+
var _d = false;
|
|
15
|
+
var _s, _e;
|
|
16
|
+
try {
|
|
17
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
18
|
+
_arr.push(_s.value);
|
|
19
|
+
if (i && _arr.length === i) break;
|
|
20
|
+
}
|
|
21
|
+
} catch (err) {
|
|
22
|
+
_d = true;
|
|
23
|
+
_e = err;
|
|
24
|
+
} finally{
|
|
25
|
+
try {
|
|
26
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
27
|
+
} finally{
|
|
28
|
+
if (_d) throw _e;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return _arr;
|
|
32
|
+
}
|
|
33
|
+
function _non_iterable_rest() {
|
|
34
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
35
|
+
}
|
|
36
|
+
function _sliced_to_array(arr, i) {
|
|
37
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
38
|
+
}
|
|
39
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
40
|
+
if (!o) return;
|
|
41
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
42
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
43
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
44
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
45
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
46
|
+
}
|
|
47
|
+
import { AudioOutlined, FileImageOutlined, FileTextFilled, FolderOpenOutlined, PictureOutlined, VideoCameraOutlined } from "@ant-design/icons";
|
|
48
|
+
import { Button, Modal, Tooltip } from "antd";
|
|
49
|
+
import React, { useCallback, useContext, useMemo, useState } from "react";
|
|
50
|
+
import { I18nContext } from "../../I18n";
|
|
51
|
+
import { isMobileDevice, isVivoOrOppoDevice, kbToSize } from "./utils";
|
|
52
|
+
/**
|
|
53
|
+
* 移动设备默认的文件类型 accept 值
|
|
54
|
+
*/ var MOBILE_DEFAULT_ACCEPT = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,.csv,image/*,text/plain,application/x-zip-compressed';
|
|
5
55
|
var FILE_SIZE_UNITS = {
|
|
6
56
|
KB: 1024,
|
|
7
57
|
MB: 1024 * 1024
|
|
@@ -82,17 +132,155 @@ export var AttachmentSupportedFormatsContent = function(param) {
|
|
|
82
132
|
});
|
|
83
133
|
};
|
|
84
134
|
export var AttachmentButtonPopover = function(param) {
|
|
85
|
-
var children = param.children, supportedFormat = param.supportedFormat;
|
|
135
|
+
var children = param.children, supportedFormat = param.supportedFormat, onFileSelect = param.onFileSelect, _param_allowMultiple = param.allowMultiple, allowMultiple = _param_allowMultiple === void 0 ? true : _param_allowMultiple;
|
|
136
|
+
var _useState = _sliced_to_array(useState(false), 2), modalOpen = _useState[0], setModalOpen = _useState[1];
|
|
137
|
+
var locale = useContext(I18nContext).locale;
|
|
138
|
+
var isVivoOrOppo = useMemo(function() {
|
|
139
|
+
return isVivoOrOppoDevice();
|
|
140
|
+
}, []);
|
|
141
|
+
var isMobile = useMemo(function() {
|
|
142
|
+
return isMobileDevice();
|
|
143
|
+
}, []);
|
|
144
|
+
var trigger = useMemo(function() {
|
|
145
|
+
return isVivoOrOppo ? [
|
|
146
|
+
'click'
|
|
147
|
+
] : [
|
|
148
|
+
'hover',
|
|
149
|
+
'click'
|
|
150
|
+
];
|
|
151
|
+
}, [
|
|
152
|
+
isVivoOrOppo
|
|
153
|
+
]);
|
|
154
|
+
var format = supportedFormat || SupportedFileFormats.image;
|
|
155
|
+
var extensions = format.extensions || [];
|
|
156
|
+
/**
|
|
157
|
+
* 根据支持的格式获取 accept 属性值
|
|
158
|
+
*/ var getAcceptValue = useCallback(function(forGallery) {
|
|
159
|
+
// 如果是移动设备,返回默认的 accept 值
|
|
160
|
+
if (isMobile || forGallery) {
|
|
161
|
+
return MOBILE_DEFAULT_ACCEPT;
|
|
162
|
+
}
|
|
163
|
+
// 打开文件,使用具体扩展名列表
|
|
164
|
+
return extensions.length > 0 ? extensions.map(function(ext) {
|
|
165
|
+
return ".".concat(ext);
|
|
166
|
+
}).join(',') : MOBILE_DEFAULT_ACCEPT;
|
|
167
|
+
}, [
|
|
168
|
+
extensions,
|
|
169
|
+
isMobile
|
|
170
|
+
]);
|
|
171
|
+
/**
|
|
172
|
+
* 创建文件输入并触发选择
|
|
173
|
+
*/ var triggerFileInput = useCallback(function(forGallery) {
|
|
174
|
+
var accept = getAcceptValue(forGallery);
|
|
175
|
+
var input = document.createElement('input');
|
|
176
|
+
input.type = 'file';
|
|
177
|
+
input.accept = accept;
|
|
178
|
+
input.multiple = allowMultiple;
|
|
179
|
+
input.style.display = 'none';
|
|
180
|
+
input.onchange = function(e) {
|
|
181
|
+
var target = e.target;
|
|
182
|
+
if (target.files && target.files.length > 0 && onFileSelect) {
|
|
183
|
+
onFileSelect(target.files, accept);
|
|
184
|
+
}
|
|
185
|
+
// 清理
|
|
186
|
+
input.remove();
|
|
187
|
+
};
|
|
188
|
+
document.body.appendChild(input);
|
|
189
|
+
input.click();
|
|
190
|
+
setModalOpen(false);
|
|
191
|
+
}, [
|
|
192
|
+
getAcceptValue,
|
|
193
|
+
onFileSelect,
|
|
194
|
+
allowMultiple
|
|
195
|
+
]);
|
|
196
|
+
var handleClick = useCallback(function(e) {
|
|
197
|
+
if (isVivoOrOppo) {
|
|
198
|
+
e.stopPropagation();
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
setModalOpen(true);
|
|
201
|
+
}
|
|
202
|
+
}, [
|
|
203
|
+
isVivoOrOppo
|
|
204
|
+
]);
|
|
205
|
+
var handleOpenGallery = useCallback(function() {
|
|
206
|
+
triggerFileInput(true);
|
|
207
|
+
}, [
|
|
208
|
+
triggerFileInput
|
|
209
|
+
]);
|
|
210
|
+
var handleOpenFile = useCallback(function() {
|
|
211
|
+
triggerFileInput(false);
|
|
212
|
+
}, [
|
|
213
|
+
triggerFileInput
|
|
214
|
+
]);
|
|
215
|
+
if (isVivoOrOppo) {
|
|
216
|
+
return /*#__PURE__*/ React.createElement("div", {
|
|
217
|
+
onClick: function(e) {
|
|
218
|
+
if (isVivoOrOppo) {
|
|
219
|
+
e.stopPropagation();
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}, /*#__PURE__*/ React.createElement("span", {
|
|
224
|
+
onClick: handleClick
|
|
225
|
+
}, children), /*#__PURE__*/ React.createElement(Modal, {
|
|
226
|
+
open: modalOpen,
|
|
227
|
+
onCancel: function() {
|
|
228
|
+
return setModalOpen(false);
|
|
229
|
+
},
|
|
230
|
+
footer: null,
|
|
231
|
+
closable: false,
|
|
232
|
+
maskClosable: true,
|
|
233
|
+
centered: true,
|
|
234
|
+
styles: {
|
|
235
|
+
content: {
|
|
236
|
+
padding: 0
|
|
237
|
+
},
|
|
238
|
+
body: {
|
|
239
|
+
padding: 0
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
width: 120
|
|
243
|
+
}, /*#__PURE__*/ React.createElement("div", {
|
|
244
|
+
style: {
|
|
245
|
+
display: 'flex',
|
|
246
|
+
flexDirection: 'column',
|
|
247
|
+
gap: 2,
|
|
248
|
+
padding: '12px 0'
|
|
249
|
+
},
|
|
250
|
+
onClick: function(e) {
|
|
251
|
+
e.stopPropagation();
|
|
252
|
+
e.preventDefault();
|
|
253
|
+
}
|
|
254
|
+
}, /*#__PURE__*/ React.createElement(Button, {
|
|
255
|
+
color: "default",
|
|
256
|
+
variant: "text",
|
|
257
|
+
icon: /*#__PURE__*/ React.createElement(PictureOutlined, null),
|
|
258
|
+
onClick: handleOpenGallery
|
|
259
|
+
}, (locale === null || locale === void 0 ? void 0 : locale['input.openGallery']) || '打开相册'), /*#__PURE__*/ React.createElement(Button, {
|
|
260
|
+
color: "default",
|
|
261
|
+
variant: "text",
|
|
262
|
+
icon: /*#__PURE__*/ React.createElement(FolderOpenOutlined, null),
|
|
263
|
+
onClick: handleOpenFile
|
|
264
|
+
}, (locale === null || locale === void 0 ? void 0 : locale['input.openFile']) || '打开文件'))));
|
|
265
|
+
}
|
|
266
|
+
// 如果是移动设备,不显示 Tooltip
|
|
267
|
+
if (isMobile) {
|
|
268
|
+
return /*#__PURE__*/ React.createElement("span", null, children);
|
|
269
|
+
}
|
|
86
270
|
return /*#__PURE__*/ React.createElement(Tooltip, {
|
|
87
271
|
arrow: false,
|
|
88
272
|
mouseEnterDelay: 1,
|
|
89
|
-
trigger:
|
|
90
|
-
'hover',
|
|
91
|
-
'click'
|
|
92
|
-
],
|
|
273
|
+
trigger: trigger,
|
|
93
274
|
title: /*#__PURE__*/ React.createElement(AttachmentSupportedFormatsContent, {
|
|
94
275
|
supportedFormat: supportedFormat
|
|
95
276
|
})
|
|
96
|
-
}, /*#__PURE__*/ React.createElement("span",
|
|
277
|
+
}, /*#__PURE__*/ React.createElement("span", {
|
|
278
|
+
onClick: function(e) {
|
|
279
|
+
if (isVivoOrOppo) {
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
e.preventDefault();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}, children));
|
|
97
285
|
};
|
|
98
286
|
export default AttachmentButtonPopover;
|
|
@@ -470,7 +470,7 @@ var ButtonContent = function(param) {
|
|
|
470
470
|
* />
|
|
471
471
|
* ```
|
|
472
472
|
*/ export var AttachmentButton = function(param) {
|
|
473
|
-
var disabled = param.disabled, uploadImage = param.uploadImage, title = param.title, supportedFormat = param.supportedFormat, render = param.render;
|
|
473
|
+
var disabled = param.disabled, uploadImage = param.uploadImage, title = param.title, supportedFormat = param.supportedFormat, render = param.render, upload = param.upload, uploadWithResponse = param.uploadWithResponse, fileMap = param.fileMap, onFileMapChange = param.onFileMapChange, maxFileSize = param.maxFileSize, maxFileCount = param.maxFileCount, minFileCount = param.minFileCount, allowMultiple = param.allowMultiple;
|
|
474
474
|
var context = useContext(ConfigProvider.ConfigContext);
|
|
475
475
|
var prefix = context === null || context === void 0 ? void 0 : context.getPrefixCls('agentic-md-editor-attachment-button');
|
|
476
476
|
var _useStyle = useStyle(prefix), wrapSSR = _useStyle.wrapSSR, hashId = _useStyle.hashId;
|
|
@@ -484,11 +484,26 @@ var ButtonContent = function(param) {
|
|
|
484
484
|
}, /*#__PURE__*/ React.createElement(ButtonContent, {
|
|
485
485
|
title: title
|
|
486
486
|
}));
|
|
487
|
+
var handleFileSelect = function(files) {
|
|
488
|
+
// 触发文件上传
|
|
489
|
+
upLoadFileToServer(files, {
|
|
490
|
+
upload: upload,
|
|
491
|
+
uploadWithResponse: uploadWithResponse,
|
|
492
|
+
fileMap: fileMap,
|
|
493
|
+
onFileMapChange: onFileMapChange,
|
|
494
|
+
maxFileSize: maxFileSize,
|
|
495
|
+
maxFileCount: maxFileCount,
|
|
496
|
+
minFileCount: minFileCount,
|
|
497
|
+
locale: {}
|
|
498
|
+
});
|
|
499
|
+
};
|
|
487
500
|
var wrapper = render ? render({
|
|
488
501
|
children: buttonWithStyle,
|
|
489
502
|
supportedFormat: format
|
|
490
503
|
}) : /*#__PURE__*/ React.createElement(AttachmentButtonPopover, {
|
|
491
|
-
supportedFormat: format
|
|
504
|
+
supportedFormat: format,
|
|
505
|
+
onFileSelect: handleFileSelect,
|
|
506
|
+
allowMultiple: allowMultiple
|
|
492
507
|
}, buttonWithStyle);
|
|
493
508
|
return wrapSSR(/*#__PURE__*/ React.createElement("div", {
|
|
494
509
|
className: classNames("".concat(prefix), hashId, _define_property({}, "".concat(prefix, "-disabled"), disabled)),
|
|
@@ -25,3 +25,63 @@ export declare const kbToSize: (kb: number) => string;
|
|
|
25
25
|
* @returns {boolean} 是否为图片文件
|
|
26
26
|
*/
|
|
27
27
|
export declare const isImageFile: (file: File) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* 获取设备品牌
|
|
30
|
+
*
|
|
31
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
32
|
+
* @returns {string | false} 设备品牌名称,如果无法匹配则返回 false
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* getDeviceBrand() // 'vivo' | '华为' | 'iphone' | false
|
|
37
|
+
* getDeviceBrand('Mozilla/5.0...vivo...') // 'vivo'
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const getDeviceBrand: (ua?: string) => string | false;
|
|
41
|
+
/**
|
|
42
|
+
* 检测是否为 vivo 手机
|
|
43
|
+
*
|
|
44
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
45
|
+
* @returns {boolean} 是否为 vivo 设备
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* isVivoDevice() // true | false
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare const isVivoDevice: (ua?: string) => boolean;
|
|
53
|
+
/**
|
|
54
|
+
* 检测是否为 oppo 手机
|
|
55
|
+
*
|
|
56
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
57
|
+
* @returns {boolean} 是否为 oppo 设备
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* isOppoDevice() // true | false
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const isOppoDevice: (ua?: string) => boolean;
|
|
65
|
+
/**
|
|
66
|
+
* 检测是否为 vivo 或 oppo 手机
|
|
67
|
+
*
|
|
68
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
69
|
+
* @returns {boolean} 是否为 vivo 或 oppo 设备
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* isVivoOrOppoDevice() // true | false
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare const isVivoOrOppoDevice: (ua?: string) => boolean;
|
|
77
|
+
/**
|
|
78
|
+
* 检测是否为移动设备
|
|
79
|
+
*
|
|
80
|
+
* @returns {boolean} 是否为移动设备
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* isMobileDevice() // true | false
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare const isMobileDevice: () => boolean;
|
|
@@ -64,3 +64,305 @@
|
|
|
64
64
|
return fileName.endsWith(ext);
|
|
65
65
|
});
|
|
66
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* 设备品牌匹配列表
|
|
69
|
+
*/ var UA_MATCH_LIST = [
|
|
70
|
+
{
|
|
71
|
+
name: 'iphone',
|
|
72
|
+
matchList: [
|
|
73
|
+
/iPhone/i
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: '华为',
|
|
78
|
+
matchList: [
|
|
79
|
+
/HUAWEI/i,
|
|
80
|
+
/SPN-AL00/i,
|
|
81
|
+
/GLK-AL00/i,
|
|
82
|
+
/Huawei/i,
|
|
83
|
+
/HMSCore/i,
|
|
84
|
+
/HW/
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: '荣耀',
|
|
89
|
+
matchList: [
|
|
90
|
+
/HONOR/i
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'oppo',
|
|
95
|
+
matchList: [
|
|
96
|
+
/PCAM10/i,
|
|
97
|
+
/OPPO/i,
|
|
98
|
+
/PCH/,
|
|
99
|
+
/PBAM00/,
|
|
100
|
+
/PBEM00/,
|
|
101
|
+
/HeyTapBrowser/,
|
|
102
|
+
/PADT00/,
|
|
103
|
+
/PCDM10/
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'vivo',
|
|
108
|
+
matchList: [
|
|
109
|
+
/V1981A/i,
|
|
110
|
+
/vivo/i,
|
|
111
|
+
/V1818A/,
|
|
112
|
+
/V1838A/,
|
|
113
|
+
/V19/,
|
|
114
|
+
/VivoBrowser/
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: '小米',
|
|
119
|
+
matchList: [
|
|
120
|
+
/Redmi/i,
|
|
121
|
+
/HM/,
|
|
122
|
+
/MIX/i,
|
|
123
|
+
/MI/,
|
|
124
|
+
/XiaoMi/
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: '金利',
|
|
129
|
+
matchList: [
|
|
130
|
+
/GN708T/i
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'oneplus',
|
|
135
|
+
matchList: [
|
|
136
|
+
/GM1910/i,
|
|
137
|
+
/ONEPLUS/i
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'sony',
|
|
142
|
+
matchList: [
|
|
143
|
+
/SOV/i,
|
|
144
|
+
/LT29i/,
|
|
145
|
+
/Xperia/
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: '三星',
|
|
150
|
+
matchList: [
|
|
151
|
+
/SAMSUNG/i,
|
|
152
|
+
/SM-/,
|
|
153
|
+
/GT/,
|
|
154
|
+
/SCH-I939I/
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: '魅族',
|
|
159
|
+
matchList: [
|
|
160
|
+
/MZ-/,
|
|
161
|
+
/MX4/i,
|
|
162
|
+
/M355/,
|
|
163
|
+
/M353/,
|
|
164
|
+
/M351/,
|
|
165
|
+
/M811/,
|
|
166
|
+
/PRO 7-H/
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: '华硕',
|
|
171
|
+
matchList: [
|
|
172
|
+
/ASUS/
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: '美图',
|
|
177
|
+
matchList: [
|
|
178
|
+
/MP/
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: '天语',
|
|
183
|
+
matchList: [
|
|
184
|
+
/K-Touch/
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: '联想',
|
|
189
|
+
matchList: [
|
|
190
|
+
/Lenovo/i
|
|
191
|
+
]
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: '宇飞来',
|
|
195
|
+
matchList: [
|
|
196
|
+
/YU FLY/i
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: '糖果',
|
|
201
|
+
matchList: [
|
|
202
|
+
/SUGAR/i
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: '酷派',
|
|
207
|
+
matchList: [
|
|
208
|
+
/Coolpad/i
|
|
209
|
+
]
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'ecell',
|
|
213
|
+
matchList: [
|
|
214
|
+
/ecell/i
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: '詹姆士',
|
|
219
|
+
matchList: [
|
|
220
|
+
/A99A/i
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'tcl',
|
|
225
|
+
matchList: [
|
|
226
|
+
/TCL/i
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: '捷语',
|
|
231
|
+
matchList: [
|
|
232
|
+
/6000/i,
|
|
233
|
+
/V1813A/
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: '8848',
|
|
238
|
+
matchList: [
|
|
239
|
+
/8848/i
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: 'H6',
|
|
244
|
+
matchList: [
|
|
245
|
+
/H6/
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: '中兴',
|
|
250
|
+
matchList: [
|
|
251
|
+
/ZTE/i
|
|
252
|
+
]
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: '努比亚',
|
|
256
|
+
matchList: [
|
|
257
|
+
/NX/
|
|
258
|
+
]
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: '海信',
|
|
262
|
+
matchList: [
|
|
263
|
+
/HS/
|
|
264
|
+
]
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'HTC',
|
|
268
|
+
matchList: [
|
|
269
|
+
/HTC/
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
];
|
|
273
|
+
/**
|
|
274
|
+
* 获取设备品牌
|
|
275
|
+
*
|
|
276
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
277
|
+
* @returns {string | false} 设备品牌名称,如果无法匹配则返回 false
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```ts
|
|
281
|
+
* getDeviceBrand() // 'vivo' | '华为' | 'iphone' | false
|
|
282
|
+
* getDeviceBrand('Mozilla/5.0...vivo...') // 'vivo'
|
|
283
|
+
* ```
|
|
284
|
+
*/ export var getDeviceBrand = function(ua) {
|
|
285
|
+
if (typeof navigator === 'undefined' && !ua) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
var userAgent = ua || navigator.userAgent;
|
|
289
|
+
// 遍历匹配列表
|
|
290
|
+
for(var i = 0; i < UA_MATCH_LIST.length; i++){
|
|
291
|
+
var uaDetail = UA_MATCH_LIST[i];
|
|
292
|
+
for(var j = 0; j < uaDetail.matchList.length; j++){
|
|
293
|
+
var re = uaDetail.matchList[j];
|
|
294
|
+
if (re.test(userAgent)) {
|
|
295
|
+
return uaDetail.name;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// 如果匹配列表中没有找到,尝试从 Build 信息中提取
|
|
300
|
+
var brandMatch = /; ([^;]+) Build/.exec(userAgent);
|
|
301
|
+
if (brandMatch) {
|
|
302
|
+
return brandMatch[1];
|
|
303
|
+
}
|
|
304
|
+
return false;
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* 检测是否为 vivo 手机
|
|
308
|
+
*
|
|
309
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
310
|
+
* @returns {boolean} 是否为 vivo 设备
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* isVivoDevice() // true | false
|
|
315
|
+
* ```
|
|
316
|
+
*/ export var isVivoDevice = function(ua) {
|
|
317
|
+
var brand = getDeviceBrand(ua);
|
|
318
|
+
return brand === 'vivo';
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* 检测是否为 oppo 手机
|
|
322
|
+
*
|
|
323
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
324
|
+
* @returns {boolean} 是否为 oppo 设备
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```ts
|
|
328
|
+
* isOppoDevice() // true | false
|
|
329
|
+
* ```
|
|
330
|
+
*/ export var isOppoDevice = function(ua) {
|
|
331
|
+
var brand = getDeviceBrand(ua);
|
|
332
|
+
return brand === 'oppo';
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* 检测是否为 vivo 或 oppo 手机
|
|
336
|
+
*
|
|
337
|
+
* @param {string} [ua] - User Agent 字符串,如果不提供则使用 navigator.userAgent
|
|
338
|
+
* @returns {boolean} 是否为 vivo 或 oppo 设备
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* isVivoOrOppoDevice() // true | false
|
|
343
|
+
* ```
|
|
344
|
+
*/ export var isVivoOrOppoDevice = function(ua) {
|
|
345
|
+
return isVivoDevice(ua) || isOppoDevice(ua);
|
|
346
|
+
};
|
|
347
|
+
/**
|
|
348
|
+
* 检测是否为移动设备
|
|
349
|
+
*
|
|
350
|
+
* @returns {boolean} 是否为移动设备
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* isMobileDevice() // true | false
|
|
355
|
+
* ```
|
|
356
|
+
*/ export var isMobileDevice = function() {
|
|
357
|
+
if (typeof navigator === 'undefined') {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
var userAgent = navigator.userAgent.toLowerCase();
|
|
361
|
+
// 检测常见的移动设备标识
|
|
362
|
+
var mobileRegex = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile|mobile safari|micromessenger/i;
|
|
363
|
+
// 检测触摸设备
|
|
364
|
+
var hasTouchScreen = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
365
|
+
// 检测屏幕宽度(移动设备通常小于 768px)
|
|
366
|
+
var isSmallScreen = typeof window !== 'undefined' && window.innerWidth <= 768;
|
|
367
|
+
return mobileRegex.test(userAgent) || hasTouchScreen && isSmallScreen;
|
|
368
|
+
};
|
|
@@ -180,6 +180,10 @@ import { useRefFunction } from "../../Hooks/useRefFunction";
|
|
|
180
180
|
import { I18nContext } from "../../I18n";
|
|
181
181
|
import { upLoadFileToServer } from "../AttachmentButton";
|
|
182
182
|
import { SupportedFileFormats } from "../AttachmentButton/AttachmentButtonPopover";
|
|
183
|
+
import { isMobileDevice, isVivoOrOppoDevice } from "../AttachmentButton/utils";
|
|
184
|
+
/**
|
|
185
|
+
* 移动设备默认的文件类型 accept 值
|
|
186
|
+
*/ var MOBILE_DEFAULT_ACCEPT = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,.csv,image/*,text/plain,application/x-zip-compressed';
|
|
183
187
|
/**
|
|
184
188
|
* 文件上传管理器
|
|
185
189
|
*
|
|
@@ -199,9 +203,49 @@ import { SupportedFileFormats } from "../AttachmentButton/AttachmentButtonPopove
|
|
|
199
203
|
onFileMapChange === null || onFileMapChange === void 0 ? void 0 : onFileMapChange(new Map(newFileMap));
|
|
200
204
|
});
|
|
201
205
|
/**
|
|
206
|
+
* 根据支持的格式获取 accept 属性值
|
|
207
|
+
* 在移动设备上,使用默认的 accept 值
|
|
208
|
+
* 在 vivo 或 oppo 手机上,如果只支持图片格式,使用 image/* 打开相册;否则使用具体扩展名打开文件选择器
|
|
209
|
+
*/ var getAcceptValue = function() {
|
|
210
|
+
var isMobile = isMobileDevice();
|
|
211
|
+
var isVivoOrOppo = isVivoOrOppoDevice();
|
|
212
|
+
var extensions = (supportedFormat === null || supportedFormat === void 0 ? void 0 : supportedFormat.extensions) || [];
|
|
213
|
+
// 如果是移动设备,返回默认的 accept 值
|
|
214
|
+
if (isMobile) {
|
|
215
|
+
return MOBILE_DEFAULT_ACCEPT;
|
|
216
|
+
}
|
|
217
|
+
if (!isVivoOrOppo) {
|
|
218
|
+
// 非 vivo/oppo 设备,直接使用扩展名列表
|
|
219
|
+
return extensions.length > 0 ? extensions.map(function(ext) {
|
|
220
|
+
return ".".concat(ext);
|
|
221
|
+
}).join(',') : 'image/*';
|
|
222
|
+
}
|
|
223
|
+
// vivo/oppo 设备:判断是否只包含图片格式
|
|
224
|
+
var imageExtensions = [
|
|
225
|
+
'jpg',
|
|
226
|
+
'jpeg',
|
|
227
|
+
'png',
|
|
228
|
+
'gif',
|
|
229
|
+
'bmp',
|
|
230
|
+
'webp',
|
|
231
|
+
'svg'
|
|
232
|
+
];
|
|
233
|
+
var isImageOnly = extensions.length > 0 && extensions.every(function(ext) {
|
|
234
|
+
return imageExtensions.includes(ext.toLowerCase());
|
|
235
|
+
});
|
|
236
|
+
if (isImageOnly) {
|
|
237
|
+
// 只支持图片格式,使用 image/* 打开相册
|
|
238
|
+
return 'image/*';
|
|
239
|
+
}
|
|
240
|
+
// 支持其他格式,使用具体扩展名列表打开文件选择器
|
|
241
|
+
return extensions.length > 0 ? extensions.map(function(ext) {
|
|
242
|
+
return ".".concat(ext);
|
|
243
|
+
}).join(',') : 'image/*';
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
202
246
|
* 上传图片
|
|
203
247
|
*/ var uploadImage = useRefFunction(/*#__PURE__*/ _async_to_generator(function() {
|
|
204
|
-
var
|
|
248
|
+
var isUploading, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, file, currentFileCount, errorMsg, input, _attachment_allowMultiple;
|
|
205
249
|
return _ts_generator(this, function(_state) {
|
|
206
250
|
// 检查是否有文件正在上传中
|
|
207
251
|
isUploading = false;
|
|
@@ -245,7 +289,7 @@ import { SupportedFileFormats } from "../AttachmentButton/AttachmentButtonPopove
|
|
|
245
289
|
input = document.createElement('input');
|
|
246
290
|
input.id = 'uploadImage' + '_' + Math.random();
|
|
247
291
|
input.type = 'file';
|
|
248
|
-
input.accept = (
|
|
292
|
+
input.accept = getAcceptValue();
|
|
249
293
|
input.multiple = (_attachment_allowMultiple = attachment === null || attachment === void 0 ? void 0 : attachment.allowMultiple) !== null && _attachment_allowMultiple !== void 0 ? _attachment_allowMultiple : true;
|
|
250
294
|
input.style.display = 'none';
|
|
251
295
|
input.onchange = /*#__PURE__*/ function() {
|