feffery_antd_components 0.1.7 → 0.1.8
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/DESCRIPTION +1 -1
- package/Project.toml +1 -1
- package/README.md +2 -2
- package/feffery_antd_components/AntdDraggerUpload.py +37 -3
- package/feffery_antd_components/AntdTable.py +20 -7
- package/feffery_antd_components/AntdUpload.py +27 -3
- package/feffery_antd_components/feffery_antd_components.min.js +2 -2
- package/feffery_antd_components/metadata.json +274 -28
- package/feffery_antd_components/package-info.json +1 -1
- package/package.json +1 -1
- package/src/FefferyAntdComponents.jl +3 -3
- package/src/jl/'feffery'_antddraggerupload.jl +21 -2
- package/src/jl/'feffery'_antdtable.jl +12 -6
- package/src/jl/'feffery'_antdupload.jl +14 -1
- package/src/lib/components/AntdDraggerUpload.react.js +205 -65
- package/src/lib/components/AntdTable.react.js +72 -15
- package/src/lib/components/AntdUpload.react.js +168 -54
- package/usage.py +68 -93
|
@@ -27,6 +27,7 @@ const AntdUpload = (props) => {
|
|
|
27
27
|
multiple,
|
|
28
28
|
directory,
|
|
29
29
|
failedTooltipInfo,
|
|
30
|
+
listUploadTaskRecord,
|
|
30
31
|
loading_state,
|
|
31
32
|
setProps
|
|
32
33
|
} = props;
|
|
@@ -34,7 +35,6 @@ const AntdUpload = (props) => {
|
|
|
34
35
|
uploadId = uploadId || uuid;
|
|
35
36
|
|
|
36
37
|
const [fileList, updateFileList] = useState([]);
|
|
37
|
-
const [lastFileError, updateLastFileError] = useState(false);
|
|
38
38
|
|
|
39
39
|
let uploadProps = {
|
|
40
40
|
name: 'file',
|
|
@@ -52,87 +52,158 @@ const AntdUpload = (props) => {
|
|
|
52
52
|
if (fileTypes.indexOf(file.name.split('.')[file.name.split('.').length - 1]) === -1) {
|
|
53
53
|
message.error(`上传失败,${file.name} 文件格式不符合要求!`);
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
return sizeCheck && fileTypes.indexOf(file.name.split('.')[file.name.split('.').length - 1]) !== -1;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
updateLastFileError(!sizeCheck)
|
|
60
59
|
return sizeCheck;
|
|
61
60
|
},
|
|
62
61
|
onChange(info) {
|
|
63
62
|
|
|
64
|
-
|
|
63
|
+
// 计算最近一次任务的子任务数量
|
|
64
|
+
let lastTaskCount
|
|
65
|
+
if (info.file.status === 'removed') {
|
|
66
|
+
lastTaskCount = 0
|
|
67
|
+
} else {
|
|
68
|
+
lastTaskCount = info.fileList.length - listUploadTaskRecord.length;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 当上传模式为multiple或directory时
|
|
72
|
+
if (multiple || directory) {
|
|
73
|
+
// 若当前事件为removed
|
|
74
|
+
if (info.file.status === 'removed') {
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
fileName: file.name,
|
|
77
|
-
fileSize: file.size,
|
|
78
|
-
completeTimestamp: new Date().getTime(),
|
|
79
|
-
taskStatus: 'success',
|
|
80
|
-
taskId: uploadId
|
|
81
|
-
}
|
|
76
|
+
// 更新任务记录
|
|
77
|
+
setProps({
|
|
78
|
+
listUploadTaskRecord: info.fileList.map(
|
|
79
|
+
(file) => {
|
|
80
|
+
return {
|
|
81
|
+
fileName: file.name,
|
|
82
|
+
fileSize: file.size,
|
|
83
|
+
completeTimestamp: new Date().getTime(),
|
|
84
|
+
taskStatus: file.status === 'done' ? 'success' : 'failed',
|
|
85
|
+
taskId: uploadId
|
|
82
86
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
})
|
|
86
90
|
} else {
|
|
91
|
+
// 其他常规事件
|
|
92
|
+
// 判断此次任务所有文件是否已上传结束(done或error)
|
|
93
|
+
if (info.fileList.slice(-lastTaskCount).every(file => file.status !== 'uploading')) {
|
|
94
|
+
|
|
95
|
+
if (info.fileList.slice(-lastTaskCount).every(file => !file.status)) {
|
|
96
|
+
} else {
|
|
97
|
+
if (lastTaskCount > 0) {
|
|
98
|
+
|
|
99
|
+
// 更新任务记录
|
|
100
|
+
setProps({
|
|
101
|
+
lastUploadTaskRecord: info.fileList.slice(-lastTaskCount).map(
|
|
102
|
+
(file) => {
|
|
103
|
+
return {
|
|
104
|
+
fileName: file.name,
|
|
105
|
+
fileSize: file.size,
|
|
106
|
+
completeTimestamp: new Date().getTime(),
|
|
107
|
+
taskStatus: file.status === 'done' ? 'success' : 'failed',
|
|
108
|
+
taskId: uploadId
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
listUploadTaskRecord: info.fileList.map(
|
|
113
|
+
(file) => {
|
|
114
|
+
return {
|
|
115
|
+
fileName: file.name,
|
|
116
|
+
fileSize: file.size,
|
|
117
|
+
completeTimestamp: new Date().getTime(),
|
|
118
|
+
taskStatus: file.status === 'done' ? 'success' : 'failed',
|
|
119
|
+
taskId: uploadId
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
} else {
|
|
132
|
+
// 单文件上传模式
|
|
133
|
+
// 若当前事件为removed
|
|
134
|
+
if (info.file.status === 'removed') {
|
|
135
|
+
|
|
87
136
|
// 更新任务记录
|
|
137
|
+
setProps({
|
|
138
|
+
listUploadTaskRecord: info.fileList.map(
|
|
139
|
+
(file) => {
|
|
140
|
+
return {
|
|
141
|
+
fileName: file.name,
|
|
142
|
+
fileSize: file.size,
|
|
143
|
+
completeTimestamp: new Date().getTime(),
|
|
144
|
+
taskStatus: file.status === 'done' ? 'success' : 'failed',
|
|
145
|
+
taskId: uploadId
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
} else if (info.file.status === 'done' || info.file.status === 'error' || !info.file.status) {
|
|
88
151
|
setProps({
|
|
89
152
|
lastUploadTaskRecord: {
|
|
90
153
|
fileName: info.file.name,
|
|
91
154
|
fileSize: info.file.size,
|
|
92
155
|
completeTimestamp: new Date().getTime(),
|
|
93
|
-
taskStatus: 'success',
|
|
156
|
+
taskStatus: info.file.status === 'done' ? 'success' : 'failed',
|
|
94
157
|
taskId: uploadId
|
|
95
|
-
}
|
|
158
|
+
},
|
|
159
|
+
listUploadTaskRecord: info.fileList.map(
|
|
160
|
+
(file) => {
|
|
161
|
+
return {
|
|
162
|
+
fileName: file.name,
|
|
163
|
+
fileSize: file.size,
|
|
164
|
+
completeTimestamp: new Date().getTime(),
|
|
165
|
+
taskStatus: file.status === 'done' ? 'success' : 'failed',
|
|
166
|
+
taskId: uploadId
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
)
|
|
96
170
|
})
|
|
97
171
|
}
|
|
172
|
+
}
|
|
98
173
|
|
|
174
|
+
if (info.file.status === 'done') {
|
|
99
175
|
message.success(`${info.file.name} 上传成功!`);
|
|
100
176
|
} else if (info.file.status === 'error') {
|
|
101
|
-
|
|
102
|
-
// 更新任务记录
|
|
103
|
-
setProps({
|
|
104
|
-
lastUploadTaskRecord: {
|
|
105
|
-
fileName: info.file.name,
|
|
106
|
-
fileSize: info.file.size,
|
|
107
|
-
completeTimestamp: new Date().getTime(),
|
|
108
|
-
taskStatus: 'failed'
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
177
|
message.error(`${info.file.name} 上传失败!`);
|
|
112
178
|
}
|
|
113
179
|
|
|
114
|
-
if (lastFileError && info.fileList.length !== 0) {
|
|
115
|
-
info.fileList[info.fileList.length - 1].status = 'error'
|
|
116
180
|
|
|
117
|
-
|
|
118
|
-
|
|
181
|
+
// 获取当前上传文件列表
|
|
182
|
+
let _fileList = [...info.fileList];
|
|
119
183
|
|
|
120
|
-
|
|
121
|
-
item => {
|
|
122
|
-
if (item.status === 'error') {
|
|
123
|
-
item.response = failedTooltipInfo ? failedTooltipInfo : '上传失败'
|
|
124
|
-
}
|
|
125
|
-
return item
|
|
126
|
-
}
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
// 基于fileListMaxLength参数设置,对fileList状态进行更新
|
|
184
|
+
// 是否限制上传记录列表最大数量
|
|
130
185
|
if (fileListMaxLength) {
|
|
131
|
-
|
|
132
|
-
} else {
|
|
133
|
-
updateFileList(info.fileList)
|
|
186
|
+
_fileList = _fileList.slice(-fileListMaxLength);
|
|
134
187
|
}
|
|
135
|
-
|
|
188
|
+
|
|
189
|
+
if (lastTaskCount !== 0) {
|
|
190
|
+
_fileList = _fileList.slice(0, _fileList.length - lastTaskCount)
|
|
191
|
+
.concat(
|
|
192
|
+
_fileList.slice(-lastTaskCount).map(
|
|
193
|
+
item => {
|
|
194
|
+
if (item.status === 'error' || !item.status) {
|
|
195
|
+
|
|
196
|
+
item.status = 'error'
|
|
197
|
+
item.response = failedTooltipInfo ? failedTooltipInfo : '上传失败'
|
|
198
|
+
}
|
|
199
|
+
return item
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
updateFileList(_fileList)
|
|
206
|
+
}
|
|
136
207
|
};
|
|
137
208
|
|
|
138
209
|
// 添加accept参数
|
|
@@ -248,6 +319,48 @@ AntdUpload.propTypes = {
|
|
|
248
319
|
)
|
|
249
320
|
]),
|
|
250
321
|
|
|
322
|
+
// 用于在每次的上传任务完成后,更新当前文件列表中全部文件的上传信息
|
|
323
|
+
listUploadTaskRecord: PropTypes.oneOfType([
|
|
324
|
+
// 单文件
|
|
325
|
+
PropTypes.exact({
|
|
326
|
+
// 记录文件名称
|
|
327
|
+
fileName: PropTypes.string,
|
|
328
|
+
|
|
329
|
+
// 记录文件大小
|
|
330
|
+
fileSize: PropTypes.number,
|
|
331
|
+
|
|
332
|
+
// 记录完成时间戳信息
|
|
333
|
+
completeTimestamp: PropTypes.number,
|
|
334
|
+
|
|
335
|
+
// 记录此次上传任务的状态信息,'success'表示成功,'failed'表示失败
|
|
336
|
+
taskStatus: PropTypes.string,
|
|
337
|
+
|
|
338
|
+
// 记录本次任务的id信息,若最近一次任务状态为'failed',则不会携带此信息
|
|
339
|
+
taskId: PropTypes.string
|
|
340
|
+
|
|
341
|
+
}),
|
|
342
|
+
// 文件夹或多文件上传
|
|
343
|
+
PropTypes.arrayOf(
|
|
344
|
+
PropTypes.exact({
|
|
345
|
+
// 记录文件名称
|
|
346
|
+
fileName: PropTypes.string,
|
|
347
|
+
|
|
348
|
+
// 记录文件大小
|
|
349
|
+
fileSize: PropTypes.number,
|
|
350
|
+
|
|
351
|
+
// 记录完成时间戳信息
|
|
352
|
+
completeTimestamp: PropTypes.number,
|
|
353
|
+
|
|
354
|
+
// 记录此次上传任务的状态信息,'success'表示成功,'failed'表示失败
|
|
355
|
+
taskStatus: PropTypes.string,
|
|
356
|
+
|
|
357
|
+
// 记录本次任务的id信息,若最近一次任务状态为'failed',则不会携带此信息
|
|
358
|
+
taskId: PropTypes.string
|
|
359
|
+
|
|
360
|
+
})
|
|
361
|
+
)
|
|
362
|
+
]),
|
|
363
|
+
|
|
251
364
|
loading_state: PropTypes.shape({
|
|
252
365
|
/**
|
|
253
366
|
* Determines if the component is loading or not
|
|
@@ -274,7 +387,8 @@ AntdUpload.propTypes = {
|
|
|
274
387
|
AntdUpload.defaultProps = {
|
|
275
388
|
fileListMaxLength: null,
|
|
276
389
|
fileMaxSize: 500,
|
|
277
|
-
lastUploadTaskRecord:
|
|
390
|
+
lastUploadTaskRecord: null,
|
|
391
|
+
listUploadTaskRecord: [],
|
|
278
392
|
locale: 'zh-cn'
|
|
279
393
|
}
|
|
280
394
|
|
package/usage.py
CHANGED
|
@@ -8,145 +8,120 @@ from dash.dependencies import Input, Output, State
|
|
|
8
8
|
app = dash.Dash(__name__)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
@app.server.route('/upload/', methods=['POST'])
|
|
12
|
-
def upload():
|
|
13
|
-
'''
|
|
14
|
-
构建文件上传服务
|
|
15
|
-
:return:
|
|
16
|
-
'''
|
|
17
|
-
|
|
18
|
-
# 获取上传id参数,用于指向保存路径
|
|
19
|
-
uploadId = request.values.get('uploadId')
|
|
20
|
-
|
|
21
|
-
# 获取上传的文件名称
|
|
22
|
-
filename = request.files['file'].filename
|
|
23
|
-
|
|
24
|
-
print({'filename': filename})
|
|
25
|
-
|
|
26
|
-
return {'filename': filename}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@app.callback(
|
|
30
|
-
Output('output', 'children'),
|
|
31
|
-
Input('input', 'lastUploadTaskRecord')
|
|
32
|
-
)
|
|
33
|
-
def test(lastUploadTaskRecord):
|
|
34
|
-
if lastUploadTaskRecord:
|
|
35
|
-
print(lastUploadTaskRecord)
|
|
36
|
-
|
|
37
|
-
return dash.no_update
|
|
38
|
-
|
|
39
11
|
app.layout = html.Div(
|
|
40
12
|
[
|
|
41
13
|
|
|
42
|
-
fac.AntdUpload(
|
|
43
|
-
id='input',
|
|
44
|
-
apiUrl='/upload/',
|
|
45
|
-
directory=True
|
|
46
|
-
),
|
|
47
|
-
|
|
48
|
-
html.Div(
|
|
49
|
-
id='output'
|
|
50
|
-
),
|
|
51
|
-
|
|
52
14
|
fac.AntdTable(
|
|
53
15
|
columns=[
|
|
54
16
|
{
|
|
55
|
-
'title': '
|
|
56
|
-
'dataIndex': '
|
|
57
|
-
'width': '200px',
|
|
17
|
+
'title': '超链接示例',
|
|
18
|
+
'dataIndex': '超链接示例',
|
|
58
19
|
'renderOptions': {
|
|
59
|
-
'renderType': '
|
|
20
|
+
'renderType': 'link',
|
|
21
|
+
'renderLinkText': '点击跳转'
|
|
60
22
|
}
|
|
61
23
|
}
|
|
62
24
|
],
|
|
63
25
|
data=[
|
|
64
26
|
{
|
|
65
|
-
'
|
|
66
|
-
|
|
67
|
-
'
|
|
27
|
+
'key': i,
|
|
28
|
+
'超链接示例': {
|
|
29
|
+
'href': 'https://github.com/CNFeffery/feffery-antd-components'
|
|
68
30
|
}
|
|
69
31
|
}
|
|
70
|
-
|
|
32
|
+
for i in range(3)
|
|
33
|
+
],
|
|
71
34
|
bordered=True,
|
|
72
35
|
style={
|
|
73
|
-
'width': '
|
|
36
|
+
'width': '250px'
|
|
74
37
|
}
|
|
75
38
|
),
|
|
76
39
|
|
|
77
40
|
fac.AntdTable(
|
|
78
|
-
selectedRowKeys=['2', '4'],
|
|
79
|
-
rowSelectionWidth='4rem',
|
|
80
41
|
columns=[
|
|
81
42
|
{
|
|
82
43
|
'title': '默认的checkbox模式',
|
|
83
|
-
'dataIndex':
|
|
84
|
-
'width': '
|
|
85
|
-
'conditionStyle': '''
|
|
86
|
-
(record, index) => {
|
|
87
|
-
if (record.默认的checkbox模式 >= 10) {
|
|
88
|
-
return {
|
|
89
|
-
style: {
|
|
90
|
-
color: "red"
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (record.默认的checkbox模式 % 2 === 0) {
|
|
95
|
-
return {
|
|
96
|
-
style: {
|
|
97
|
-
backgroundColor: "#ffe7ba"
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
style: {
|
|
103
|
-
fontWeight: "bold"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
'''
|
|
108
|
-
# 'fixed': 'left'
|
|
44
|
+
'dataIndex': '默认的checkbox模式',
|
|
45
|
+
'width': '25%'
|
|
109
46
|
},
|
|
110
47
|
{
|
|
111
48
|
'title': '自定义选项的checkbox模式',
|
|
112
49
|
'dataIndex': '自定义选项的checkbox模式',
|
|
113
|
-
'width': '
|
|
50
|
+
'width': '25%'
|
|
114
51
|
},
|
|
115
52
|
{
|
|
116
53
|
'title': 'keyword模式',
|
|
117
54
|
'dataIndex': 'keyword模式',
|
|
118
|
-
'width': '
|
|
55
|
+
'width': '25%'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
'title': '数值测试',
|
|
59
|
+
'dataIndex': '数值测试',
|
|
60
|
+
'width': '25%',
|
|
61
|
+
'renderOptions': {
|
|
62
|
+
'renderType': 'custom-format'
|
|
63
|
+
}
|
|
119
64
|
}
|
|
120
65
|
],
|
|
121
|
-
sticky=True,
|
|
122
|
-
pagination={
|
|
123
|
-
'pageSize': 100
|
|
124
|
-
},
|
|
125
66
|
data=[
|
|
126
67
|
{
|
|
127
68
|
'默认的checkbox模式': i,
|
|
128
69
|
'自定义选项的checkbox模式': i,
|
|
129
|
-
'keyword模式': i
|
|
70
|
+
'keyword模式': i,
|
|
71
|
+
'数值测试': np.random.rand()
|
|
130
72
|
}
|
|
131
|
-
for i in range(
|
|
73
|
+
for i in range(20)
|
|
132
74
|
],
|
|
75
|
+
bordered=True,
|
|
133
76
|
filterOptions={
|
|
134
|
-
'默认的checkbox模式': {
|
|
135
|
-
'filterMode': 'keyword'
|
|
136
|
-
},
|
|
77
|
+
'默认的checkbox模式': {},
|
|
137
78
|
'自定义选项的checkbox模式': {
|
|
138
|
-
'filterMode': '
|
|
79
|
+
'filterMode': 'checkbox',
|
|
80
|
+
'filterCustomItems': [1, 2, 3],
|
|
81
|
+
'filterMultiple': False,
|
|
82
|
+
'filterSearch': True
|
|
139
83
|
},
|
|
140
84
|
'keyword模式': {
|
|
141
85
|
'filterMode': 'keyword'
|
|
142
86
|
}
|
|
143
87
|
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
88
|
+
pagination={
|
|
89
|
+
'pageSize': 20
|
|
90
|
+
},
|
|
91
|
+
customFormatFuncs={
|
|
92
|
+
'数值测试': '(x) => `${(x*100).toFixed(2)}%`'
|
|
93
|
+
},
|
|
94
|
+
conditionalStyleFuncs={
|
|
95
|
+
**{
|
|
96
|
+
key: '''
|
|
97
|
+
(record, index) => {
|
|
98
|
+
if ( index % 2 === 1 ) {
|
|
99
|
+
return {
|
|
100
|
+
style: {
|
|
101
|
+
backgroundColor: "rgb(250, 250, 250)"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
'''
|
|
107
|
+
for key in ['默认的checkbox模式', '自定义选项的checkbox模式', 'keyword模式']
|
|
108
|
+
},
|
|
109
|
+
'数值测试': '''
|
|
110
|
+
(record, index) => {
|
|
111
|
+
if ( record['数值测试'] <= 0.5 ) {
|
|
112
|
+
return {
|
|
113
|
+
style: {
|
|
114
|
+
background: `linear-gradient(90deg, rgb(61, 153, 112) 0%, rgb(61, 153, 112) ${record['数值测试']*100}%, white ${record['数值测试']*100}%, white 100%)`
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
style: {
|
|
120
|
+
background: `linear-gradient(90deg, rgb(255, 65, 54) 0%, rgb(255, 65, 54) ${record['数值测试']*100}%, white ${record['数值测试']*100}%, white 100%)`
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
'''
|
|
150
125
|
}
|
|
151
126
|
)
|
|
152
127
|
],
|