@aozi6666/bee-design 0.1.0
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 +73 -0
- package/build/App.d.ts +4 -0
- package/build/App.js +137 -0
- package/build/components/AutoComplete/autoComplete.d.ts +13 -0
- package/build/components/AutoComplete/autoComplete.js +123 -0
- package/build/components/AutoComplete/autoComplete.types.d.ts +19 -0
- package/build/components/AutoComplete/autoComplete.types.js +1 -0
- package/build/components/AutoComplete/autoCompleteDropdown.d.ts +13 -0
- package/build/components/AutoComplete/autoCompleteDropdown.js +17 -0
- package/build/components/AutoComplete/index.d.ts +4 -0
- package/build/components/AutoComplete/index.js +3 -0
- package/build/components/Button/button.d.ts +6 -0
- package/build/components/Button/button.js +43 -0
- package/build/components/Button/button.types.d.ts +24 -0
- package/build/components/Button/button.types.js +19 -0
- package/build/components/Button/index.d.ts +2 -0
- package/build/components/Button/index.js +2 -0
- package/build/components/Icon/icon.d.ts +16 -0
- package/build/components/Icon/icon.js +24 -0
- package/build/components/Icon/icon.types.d.ts +6 -0
- package/build/components/Icon/icon.types.js +2 -0
- package/build/components/Icon/index.d.ts +2 -0
- package/build/components/Icon/index.js +2 -0
- package/build/components/Input/index.d.ts +4 -0
- package/build/components/Input/index.js +3 -0
- package/build/components/Input/input.d.ts +5 -0
- package/build/components/Input/input.js +32 -0
- package/build/components/Input/input.types.d.ts +16 -0
- package/build/components/Input/input.types.js +1 -0
- package/build/components/Menu/index.d.ts +10 -0
- package/build/components/Menu/index.js +9 -0
- package/build/components/Menu/menu.d.ts +34 -0
- package/build/components/Menu/menu.js +48 -0
- package/build/components/Menu/menuItem.d.ts +14 -0
- package/build/components/Menu/menuItem.js +20 -0
- package/build/components/Menu/subMenu.d.ts +11 -0
- package/build/components/Menu/subMenu.js +56 -0
- package/build/components/Progress/index.d.ts +2 -0
- package/build/components/Progress/index.js +2 -0
- package/build/components/Progress/progress.d.ts +4 -0
- package/build/components/Progress/progress.js +6 -0
- package/build/components/Progress/progress.types.d.ts +9 -0
- package/build/components/Progress/progress.types.js +2 -0
- package/build/components/Transition/index.d.ts +3 -0
- package/build/components/Transition/index.js +2 -0
- package/build/components/Transition/transition.d.ts +4 -0
- package/build/components/Transition/transition.js +18 -0
- package/build/components/Transition/transition.types.d.ts +10 -0
- package/build/components/Transition/transition.types.js +1 -0
- package/build/components/Upload/dragger.d.ts +7 -0
- package/build/components/Upload/dragger.js +42 -0
- package/build/components/Upload/index.d.ts +2 -0
- package/build/components/Upload/index.js +2 -0
- package/build/components/Upload/native/axios-react.d.ts +2 -0
- package/build/components/Upload/native/axios-react.js +99 -0
- package/build/components/Upload/native/from-html.d.ts +2 -0
- package/build/components/Upload/native/from-html.js +5 -0
- package/build/components/Upload/upload.d.ts +13 -0
- package/build/components/Upload/upload.js +192 -0
- package/build/components/Upload/upload.types.d.ts +48 -0
- package/build/components/Upload/upload.types.js +3 -0
- package/build/components/Upload/uploadList.d.ts +8 -0
- package/build/components/Upload/uploadList.js +13 -0
- package/build/hooks/useClickOutside.d.ts +3 -0
- package/build/hooks/useClickOutside.js +18 -0
- package/build/hooks/useDebounce.d.ts +2 -0
- package/build/hooks/useDebounce.js +14 -0
- package/build/index.css +856 -0
- package/build/index.css.map +1 -0
- package/build/index.d.ts +9 -0
- package/build/index.js +12 -0
- package/build/main.d.ts +1 -0
- package/build/main.js +7 -0
- package/build/setupTests.d.ts +1 -0
- package/build/setupTests.js +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import UploadList from './uploadList';
|
|
5
|
+
import Dragger from './dragger';
|
|
6
|
+
/**
|
|
7
|
+
* 通过点击或者拖拽上传文件
|
|
8
|
+
* ### 引用方法
|
|
9
|
+
*
|
|
10
|
+
* ~~~js
|
|
11
|
+
* import { Upload } from 'vikingship'
|
|
12
|
+
* ~~~
|
|
13
|
+
*/
|
|
14
|
+
export const Upload = (props) => {
|
|
15
|
+
const { action, defaultFileList, beforeUpload, onProgress, onSuccess, onError, onChange, onRemove, name = 'file', headers, data, withCredentials, accept, multiple, children, drag, } = props;
|
|
16
|
+
// 文件输入框 ref 引用
|
|
17
|
+
const fileInput = useRef(null);
|
|
18
|
+
// (子组件)页面上 正在显示 的 上传文件列表
|
|
19
|
+
const [fileList, setFileList] = useState(defaultFileList || []);
|
|
20
|
+
// **`UploadList`** 子组件: 从 上传文件列表fileList 渲染一堆 列表项 class
|
|
21
|
+
// 上传列表状态更新器
|
|
22
|
+
const updateFileList = (updateFile, updateObj) => {
|
|
23
|
+
setFileList(prevList => {
|
|
24
|
+
return prevList.map(file => {
|
|
25
|
+
// 在列表里找到同一个 uid 文件
|
|
26
|
+
if (file.uid === updateFile.uid) {
|
|
27
|
+
// 更新部分字段
|
|
28
|
+
return { ...file, ...updateObj };
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return file;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
// 点击上传回调:用户点击 上传文件 输入框
|
|
37
|
+
const handleClick = () => {
|
|
38
|
+
// 用 ref 拿到“隐藏的” input元素
|
|
39
|
+
if (fileInput.current) {
|
|
40
|
+
// 调用浏览器的原生能力: 弹出 系统级文件选择框
|
|
41
|
+
fileInput.current.click();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
// 文件上传回调:当有文件传来的时候触发
|
|
45
|
+
const handleFileChange = (e) => {
|
|
46
|
+
// 获取文件列表:类型为 FileList
|
|
47
|
+
const files = e.target.files;
|
|
48
|
+
if (!files) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// 调用 上传文件函数(含发送请求):决定 每个/不同文件 怎么处理
|
|
52
|
+
uploadFiles(files);
|
|
53
|
+
// 清空 文件输入框
|
|
54
|
+
if (fileInput.current) {
|
|
55
|
+
fileInput.current.value = '';
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
// 传递给 UploadList 子组件的 回调函数
|
|
59
|
+
const handleRemove = (file) => {
|
|
60
|
+
setFileList((prevList) => {
|
|
61
|
+
return prevList.filter(item => item.uid !== file.uid);
|
|
62
|
+
});
|
|
63
|
+
if (onRemove) {
|
|
64
|
+
onRemove(file);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
// 函数: 上传文件: 决定 每个文件 上传
|
|
68
|
+
// (beforeUpload: “上传前钩子”)
|
|
69
|
+
const uploadFiles = (files, test) => {
|
|
70
|
+
// 传来的文件列表 FileList类型,不是数组 =》 转为数组
|
|
71
|
+
let postFiles = Array.from(files);
|
|
72
|
+
//
|
|
73
|
+
if (test) {
|
|
74
|
+
console.log('drag', postFiles[0]);
|
|
75
|
+
}
|
|
76
|
+
// 遍历 数组 每一项
|
|
77
|
+
postFiles.forEach(file => {
|
|
78
|
+
// 没有配置 beforeUpload,直接上传
|
|
79
|
+
if (!beforeUpload) {
|
|
80
|
+
post(file);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// 用户使用了 beforeUpload => 需要等待 用户的异步处理结果(例如压缩图片)
|
|
84
|
+
// beforeUpload(file)用户在钩子里写的回调: 会返回 Promise<newFile>
|
|
85
|
+
// 获取 Promise<newFile> 用这个处理完的 File 对象发送
|
|
86
|
+
// 执行用户传进来的回调函数,并把它的返回值接住,放进 result
|
|
87
|
+
const result = beforeUpload(file);
|
|
88
|
+
//
|
|
89
|
+
if (result && result instanceof Promise) {
|
|
90
|
+
// 获取 异步回调 reslove(newFile)后的 newFile
|
|
91
|
+
result.then(processedFile => {
|
|
92
|
+
post(processedFile); // 发送用户异步处理完的 新File文件
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
else if (result !== false) {
|
|
96
|
+
// 用户使用了拦截:return false ==> 永远不会触发 post, 不上传
|
|
97
|
+
// 返回 true → 上传原文件
|
|
98
|
+
post(file);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
// 函数: 发axios请求
|
|
104
|
+
const post = (file) => {
|
|
105
|
+
// 改造浏览器原生File,创建 内部文件对象 `_file` (文件本体 + 上传状态)
|
|
106
|
+
let _file = {
|
|
107
|
+
uid: Date.now() + 'upload-file',
|
|
108
|
+
status: 'ready',
|
|
109
|
+
name: file.name,
|
|
110
|
+
size: file.size,
|
|
111
|
+
percent: 0,
|
|
112
|
+
raw: file
|
|
113
|
+
};
|
|
114
|
+
// 请求发送之前放进 fileList,列表先显示出来
|
|
115
|
+
setFileList(prevList => {
|
|
116
|
+
return [_file, ...prevList];
|
|
117
|
+
});
|
|
118
|
+
// 2) 构建 `FormData`
|
|
119
|
+
const formData = new FormData();
|
|
120
|
+
formData.append(name || 'file', file);
|
|
121
|
+
// 如果传了 `data`, 额外字段也 append 进 FormData
|
|
122
|
+
// (例如 `userId`、`token`)
|
|
123
|
+
if (data) {
|
|
124
|
+
Object.keys(data).forEach(key => {
|
|
125
|
+
formData.append(key, data[key]);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// 使用 axios 发送 POST 请求
|
|
129
|
+
axios.post(action, formData, {
|
|
130
|
+
headers: {
|
|
131
|
+
...headers,
|
|
132
|
+
'Content-Type': 'multipart/form-data'
|
|
133
|
+
},
|
|
134
|
+
// 跨域请求 凭证信息(Cookie)
|
|
135
|
+
// 需要 后端 允许跨域携带凭证: Access-Control-Allow-Credentials: true
|
|
136
|
+
withCredentials,
|
|
137
|
+
// axios 提供的 请求配置回调(“系统回调”)
|
|
138
|
+
// (不需要手动调用)上传过程中,axios 内部自动不断触发 onUploadProgress
|
|
139
|
+
onUploadProgress: (e) => {
|
|
140
|
+
// 把字节进度算成百分比
|
|
141
|
+
const total = e.total ?? 0;
|
|
142
|
+
const percentage = total ? Math.round((e.loaded * 100) / total) : 0;
|
|
143
|
+
// 更新 fileList 中这条文件的 percent/status
|
|
144
|
+
if (percentage < 100) {
|
|
145
|
+
// 更新 React state(驱动UI):让 UploadList 子组件重新渲染
|
|
146
|
+
updateFileList(_file, { percent: percentage, status: 'uploading' });
|
|
147
|
+
// 更新当前 _file 对象,保证传给回调的值是新的
|
|
148
|
+
_file.status = 'uploading';
|
|
149
|
+
_file.percent = percentage;
|
|
150
|
+
// 将 axios 的 onUploadProgress 得到的上传进度结果,包装一层
|
|
151
|
+
// 提供给 Upload 组件的外部使用者 外部钩子onProgress:给组件外部使用
|
|
152
|
+
if (onProgress) {
|
|
153
|
+
onProgress(percentage, _file);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}).then(resp => {
|
|
158
|
+
// 成功时:更新React UI内部状态
|
|
159
|
+
updateFileList(_file, { status: 'success', response: resp.data });
|
|
160
|
+
// 更新当前 _file 对象,保证传给回调的值是新的
|
|
161
|
+
_file.status = 'success';
|
|
162
|
+
_file.response = resp.data;
|
|
163
|
+
// 通知外部 onSuccess / onChange 钩子
|
|
164
|
+
if (onSuccess) {
|
|
165
|
+
onSuccess(resp.data, _file);
|
|
166
|
+
}
|
|
167
|
+
if (onChange) {
|
|
168
|
+
onChange(_file);
|
|
169
|
+
}
|
|
170
|
+
}).catch(err => {
|
|
171
|
+
// 失败时:更新React UI内部状态
|
|
172
|
+
updateFileList(_file, { status: 'error', error: err });
|
|
173
|
+
// 更新当前 _file 对象,保证传给回调的值是新的
|
|
174
|
+
_file.status = 'error';
|
|
175
|
+
_file.error = err;
|
|
176
|
+
//
|
|
177
|
+
if (onError) {
|
|
178
|
+
onError(err, _file);
|
|
179
|
+
}
|
|
180
|
+
if (onChange) {
|
|
181
|
+
onChange(_file);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
return (_jsxs("div", { className: "viking-upload-component", children: [_jsxs("div", { className: "viking-upload-input", style: { display: 'inline-block' }, onClick: handleClick, children: [children, drag ?
|
|
186
|
+
// 传给 Dragger子组件回调函数 onFile
|
|
187
|
+
// 当用户拖拽文件,Dragger 的onDrop状态触发 onFile(files)
|
|
188
|
+
_jsx(Dragger, { onFile: (files) => { uploadFiles(files, true); }, children: children }) :
|
|
189
|
+
// 如果 drag = false, 直接渲染 children(普通模式)
|
|
190
|
+
children, _jsx("input", { className: "viking-file-input", style: { display: 'none' }, ref: fileInput, onChange: handleFileChange, type: "file", accept: accept, multiple: multiple })] }), _jsx(UploadList, { fileList: fileList, onRemove: handleRemove })] }));
|
|
191
|
+
};
|
|
192
|
+
export default Upload;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type UploadFileStatus = 'ready' | 'uploading' | 'success' | 'error';
|
|
2
|
+
export interface UploadFile {
|
|
3
|
+
uid: string;
|
|
4
|
+
size: number;
|
|
5
|
+
name: string;
|
|
6
|
+
status?: UploadFileStatus;
|
|
7
|
+
percent: number;
|
|
8
|
+
raw?: File;
|
|
9
|
+
response?: any;
|
|
10
|
+
error?: any;
|
|
11
|
+
}
|
|
12
|
+
export interface UploadProps {
|
|
13
|
+
/** 必选参数, 上传的地址 */
|
|
14
|
+
action: string;
|
|
15
|
+
/** 上传的文件列表 */
|
|
16
|
+
defaultFileList?: UploadFile[];
|
|
17
|
+
/** 上传文件之前的钩子,返回 false 或 Promise 可以阻止上传 */
|
|
18
|
+
beforeUpload?: (file: File) => boolean | Promise<File>;
|
|
19
|
+
/** 文件上传时的钩子 */
|
|
20
|
+
onProgress?: (percentage: number, file: UploadFile) => void;
|
|
21
|
+
/** 文件上传成功时的钩子 */
|
|
22
|
+
onSuccess?: (data: any, file: UploadFile) => void;
|
|
23
|
+
/** 文件上传失败时的钩子 */
|
|
24
|
+
onError?: (err: any, file: UploadFile) => void;
|
|
25
|
+
/** 文件状态改变时的钩子,上传成功或者失败时都会被调用 */
|
|
26
|
+
onChange?: (file: UploadFile) => void;
|
|
27
|
+
/** 文件列表移除文件时的钩子 */
|
|
28
|
+
onRemove?: (file: UploadFile) => void;
|
|
29
|
+
/** 设置上传的请求头部 */
|
|
30
|
+
headers?: {
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
};
|
|
33
|
+
/** 上传的文件字段名 */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** 上传时附带的额外参数 */
|
|
36
|
+
data?: {
|
|
37
|
+
[key: string]: any;
|
|
38
|
+
};
|
|
39
|
+
/** 是否发送 cookie 凭证信息 */
|
|
40
|
+
withCredentials?: boolean;
|
|
41
|
+
/** 可选参数, 接受上传的文件类型 */
|
|
42
|
+
accept?: string;
|
|
43
|
+
/** 是否支持多选文件 */
|
|
44
|
+
multiple?: boolean;
|
|
45
|
+
/** 是否支持拖拽上传 */
|
|
46
|
+
drag?: boolean;
|
|
47
|
+
children?: React.ReactNode;
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FC } from 'react';
|
|
2
|
+
import type { UploadFile } from './upload.types';
|
|
3
|
+
interface UploadListProps {
|
|
4
|
+
fileList: UploadFile[];
|
|
5
|
+
onRemove: (_file: UploadFile) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const UploadList: FC<UploadListProps>;
|
|
8
|
+
export default UploadList;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Icon from '../Icon/icon';
|
|
3
|
+
import Progress from '../Progress/progress';
|
|
4
|
+
export const UploadList = (props) => {
|
|
5
|
+
// 从 Prop 中取出 fileList 文件列表 和 回调
|
|
6
|
+
const { fileList, onRemove, } = props;
|
|
7
|
+
console.log('firelist', fileList);
|
|
8
|
+
return (_jsx("ul", { className: "viking-upload-list", children: fileList.map(item => {
|
|
9
|
+
return (_jsxs("li", { className: "viking-upload-list-item", children: [_jsxs("span", { className: `file-name file-name-${item.status}`, children: [_jsx(Icon, { icon: "file-alt", theme: "secondary" }), item.name] }), _jsxs("span", { className: "file-status", children: [(item.status === 'uploading' || item.status === 'ready') && _jsx(Icon, { icon: "spinner", spin: true, theme: "primary" }), item.status === 'success' && _jsx(Icon, { icon: "check-circle", theme: "success" }), item.status === 'error' && _jsx(Icon, { icon: "times-circle", theme: "danger" })] }), _jsx("span", { className: "file-actions", children: _jsx(Icon, { icon: "times", onClick: () => { onRemove(item); } }) }), item.status === 'uploading' &&
|
|
10
|
+
_jsx(Progress, { percent: item.percent || 0 })] }, item.uid));
|
|
11
|
+
}) }));
|
|
12
|
+
};
|
|
13
|
+
export default UploadList;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
function useClickOutside(ref, handler) {
|
|
3
|
+
useEffect(() => {
|
|
4
|
+
const listener = (event) => {
|
|
5
|
+
const el = ref?.current;
|
|
6
|
+
if (!el)
|
|
7
|
+
return;
|
|
8
|
+
if (el.contains(event.target))
|
|
9
|
+
return;
|
|
10
|
+
handler(event);
|
|
11
|
+
};
|
|
12
|
+
document.addEventListener('click', listener);
|
|
13
|
+
return () => {
|
|
14
|
+
document.removeEventListener('click', listener);
|
|
15
|
+
};
|
|
16
|
+
}, [ref, handler]);
|
|
17
|
+
}
|
|
18
|
+
export default useClickOutside;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
function useDebounce(value, delay = 300) {
|
|
3
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const handler = window.setTimeout(() => {
|
|
6
|
+
setDebouncedValue(value);
|
|
7
|
+
}, delay);
|
|
8
|
+
return () => {
|
|
9
|
+
clearTimeout(handler);
|
|
10
|
+
};
|
|
11
|
+
}, [value, delay]);
|
|
12
|
+
return debouncedValue;
|
|
13
|
+
}
|
|
14
|
+
export default useDebounce;
|