Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
nni
Commits
744885eb
Commit
744885eb
authored
Nov 01, 2018
by
Lijiao
Committed by
chicm-ms
Nov 01, 2018
Browse files
Update WebUI (#295)
parent
c5c0fa2e
Changes
67
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
928 additions
and
70 deletions
+928
-70
src/webui/src/components/overview/SearchSpace.tsx
src/webui/src/components/overview/SearchSpace.tsx
+24
-0
src/webui/src/components/overview/SuccessTable.tsx
src/webui/src/components/overview/SuccessTable.tsx
+148
-0
src/webui/src/components/overview/Title1.tsx
src/webui/src/components/overview/Title1.tsx
+27
-0
src/webui/src/components/overview/TrialProfile.tsx
src/webui/src/components/overview/TrialProfile.tsx
+35
-0
src/webui/src/components/trial-detail/Duration.tsx
src/webui/src/components/trial-detail/Duration.tsx
+155
-0
src/webui/src/components/trial-detail/Para.tsx
src/webui/src/components/trial-detail/Para.tsx
+34
-58
src/webui/src/components/trial-detail/TableList.tsx
src/webui/src/components/trial-detail/TableList.tsx
+385
-0
src/webui/src/index.css
src/webui/src/index.css
+57
-3
src/webui/src/index.tsx
src/webui/src/index.tsx
+5
-8
src/webui/src/static/const.ts
src/webui/src/static/const.ts
+1
-1
src/webui/src/static/font/SegoePro-Regular.ttf
src/webui/src/static/font/SegoePro-Regular.ttf
+0
-0
src/webui/src/static/function.ts
src/webui/src/static/function.ts
+57
-0
src/webui/src/static/img/icon/1.png
src/webui/src/static/img/icon/1.png
+0
-0
src/webui/src/static/img/icon/10.png
src/webui/src/static/img/icon/10.png
+0
-0
src/webui/src/static/img/icon/11.png
src/webui/src/static/img/icon/11.png
+0
-0
src/webui/src/static/img/icon/2.png
src/webui/src/static/img/icon/2.png
+0
-0
src/webui/src/static/img/icon/3.png
src/webui/src/static/img/icon/3.png
+0
-0
src/webui/src/static/img/icon/4.png
src/webui/src/static/img/icon/4.png
+0
-0
src/webui/src/static/img/icon/5.png
src/webui/src/static/img/icon/5.png
+0
-0
src/webui/src/static/img/icon/6.png
src/webui/src/static/img/icon/6.png
+0
-0
No files found.
src/webui/src/components/overview/SearchSpace.tsx
0 → 100644
View file @
744885eb
import
*
as
React
from
'
react
'
;
interface
SearchspaceProps
{
searchSpace
:
object
;
}
class
SearchSpace
extends
React
.
Component
<
SearchspaceProps
,
{}
>
{
constructor
(
props
:
SearchspaceProps
)
{
super
(
props
);
}
render
()
{
const
{
searchSpace
}
=
this
.
props
;
return
(
<
pre
className
=
"experiment searchSpace"
style
=
{
{
paddingLeft
:
20
}
}
>
{
JSON
.
stringify
(
searchSpace
,
null
,
4
)
}
</
pre
>
);
}
}
export
default
SearchSpace
;
\ No newline at end of file
src/webui/src/components/overview/SuccessTable.tsx
0 → 100644
View file @
744885eb
import
*
as
React
from
'
react
'
;
import
JSONTree
from
'
react-json-tree
'
;
import
{
Table
}
from
'
antd
'
;
import
{
TableObj
}
from
'
../../static/interface
'
;
import
{
convertDuration
}
from
'
../../static/function
'
;
import
'
../../static/style/tableStatus.css
'
;
interface
SuccessTableProps
{
tableSource
:
Array
<
TableObj
>
;
}
class
SuccessTable
extends
React
.
Component
<
SuccessTableProps
,
{}
>
{
constructor
(
props
:
SuccessTableProps
)
{
super
(
props
);
}
render
()
{
const
{
tableSource
}
=
this
.
props
;
let
bgColor
=
''
;
const
columns
=
[{
title
:
'
Number
'
,
dataIndex
:
'
sequenceId
'
,
key
:
'
sequenceId
'
,
width
:
60
,
className
:
'
tableHead
'
,
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
return
(
<
span
>
#
{
record
.
sequenceId
}
</
span
>
);
}
},
{
title
:
'
Id
'
,
dataIndex
:
'
id
'
,
key
:
'
id
'
,
width
:
150
,
className
:
'
tableHead
'
},
{
title
:
'
Duration
'
,
dataIndex
:
'
duration
'
,
key
:
'
duration
'
,
width
:
150
,
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
let
duration
;
if
(
record
.
duration
)
{
duration
=
convertDuration
(
record
.
duration
);
}
return
(
<
div
>
{
duration
}
</
div
>
);
},
},
{
title
:
'
Status
'
,
dataIndex
:
'
status
'
,
key
:
'
status
'
,
width
:
150
,
className
:
'
tableStatus
'
,
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
bgColor
=
record
.
status
;
return
(
<
div
className
=
{
`
${
bgColor
}
commonStyle`
}
>
{
record
.
status
}
</
div
>
);
}
},
{
title
:
'
Default Metric
'
,
dataIndex
:
'
acc
'
,
key
:
'
acc
'
,
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
return
(
<
div
>
{
record
.
acc
?
record
.
acc
.
toFixed
(
6
)
:
record
.
acc
}
</
div
>
);
}
// width: 150
}];
const
openRow
=
(
record
:
TableObj
)
=>
{
let
isHasParameters
=
true
;
if
(
record
.
description
.
parameters
.
error
)
{
isHasParameters
=
false
;
}
const
openRowDataSource
=
{
parameters
:
record
.
description
.
parameters
};
let
isLogLink
:
boolean
=
false
;
const
logPathRow
=
record
.
description
.
logPath
;
if
(
record
.
description
.
isLink
!==
undefined
)
{
isLogLink
=
true
;
}
return
(
<
pre
id
=
"description"
className
=
"hyperpar"
>
{
isHasParameters
?
<
JSONTree
hideRoot
=
{
true
}
shouldExpandNode
=
{
()
=>
true
}
// default expandNode
getItemString
=
{
()
=>
(<
span
/>)
}
// remove the {} items
data
=
{
openRowDataSource
}
/>
:
<
div
className
=
"logpath"
>
<
span
className
=
"logName"
>
Error:
</
span
>
<
span
className
=
"error"
>
'This trial's parameters are not available.'
</
span
>
</
div
>
}
{
isLogLink
?
<
div
className
=
"logpath"
>
<
span
className
=
"logName"
>
logPath:
</
span
>
<
a
className
=
"logContent logHref"
href
=
{
logPathRow
}
target
=
"_blank"
>
{
logPathRow
}
</
a
>
</
div
>
:
<
div
className
=
"logpath"
>
<
span
className
=
"logName"
>
logPath:
</
span
>
<
span
className
=
"logContent"
>
{
logPathRow
}
</
span
>
</
div
>
}
</
pre
>
);
};
return
(
<
div
className
=
"tabScroll"
>
<
Table
columns
=
{
columns
}
expandedRowRender
=
{
openRow
}
dataSource
=
{
tableSource
}
className
=
"commonTableStyle"
pagination
=
{
false
}
/>
</
div
>
);
}
}
export
default
SuccessTable
;
\ No newline at end of file
src/webui/src/components/overview/Title1.tsx
0 → 100644
View file @
744885eb
import
*
as
React
from
'
react
'
;
interface
Title1Props
{
text
:
string
;
icon
:
string
;
}
class
Title1
extends
React
.
Component
<
Title1Props
,
{}
>
{
constructor
(
props
:
Title1Props
)
{
super
(
props
);
}
render
()
{
const
{
text
,
icon
}
=
this
.
props
;
return
(
<
div
>
<
div
className
=
"panelTitle"
>
<
img
src
=
{
require
(
`../../static/img/icon/
${
icon
}
`
)
}
alt
=
"icon"
/>
<
span
>
{
text
}
</
span
>
</
div
>
</
div
>
);
}
}
export
default
Title1
;
\ No newline at end of file
src/webui/src/components/overview/TrialProfile.tsx
0 → 100644
View file @
744885eb
import
*
as
React
from
'
react
'
;
import
{
Experiment
}
from
'
../../static/interface
'
;
interface
TrialInfoProps
{
tiralProInfo
:
Experiment
;
}
class
TrialInfo
extends
React
.
Component
<
TrialInfoProps
,
{}
>
{
constructor
(
props
:
TrialInfoProps
)
{
super
(
props
);
}
render
()
{
const
{
tiralProInfo
}
=
this
.
props
;
const
showProInfo
=
[];
showProInfo
.
push
({
revision
:
tiralProInfo
.
revision
,
authorName
:
tiralProInfo
.
author
,
trialConcurrency
:
tiralProInfo
.
runConcurren
,
tuner
:
tiralProInfo
.
tuner
,
assessor
:
tiralProInfo
.
assessor
?
tiralProInfo
.
assessor
:
undefined
,
clusterMetaData
:
tiralProInfo
.
clusterMetaData
?
tiralProInfo
.
clusterMetaData
:
undefined
});
return
(
<
div
style
=
{
{
paddingLeft
:
20
}
}
>
<
pre
>
{
JSON
.
stringify
(
showProInfo
[
0
],
null
,
4
)
}
</
pre
>
</
div
>
);
}
}
export
default
TrialInfo
;
\ No newline at end of file
src/webui/src/components/trial-detail/Duration.tsx
0 → 100644
View file @
744885eb
import
*
as
React
from
'
react
'
;
import
axios
from
'
axios
'
;
import
{
MANAGER_IP
}
from
'
../../static/const
'
;
import
ReactEcharts
from
'
echarts-for-react
'
;
const
echarts
=
require
(
'
echarts/lib/echarts
'
);
require
(
'
echarts/lib/chart/bar
'
);
require
(
'
echarts/lib/component/tooltip
'
);
require
(
'
echarts/lib/component/title
'
);
echarts
.
registerTheme
(
'
my_theme
'
,
{
color
:
'
#3c8dbc
'
});
interface
Runtrial
{
trialId
:
Array
<
string
>
;
trialTime
:
Array
<
number
>
;
}
interface
DurationState
{
durationSource
:
{};
}
class
Duration
extends
React
.
Component
<
{},
DurationState
>
{
static
intervalDuration
=
1
;
public
_isMounted
=
false
;
constructor
(
props
:
{})
{
super
(
props
);
this
.
state
=
{
durationSource
:
{}
};
}
getOption
=
(
dataObj
:
Runtrial
)
=>
{
let
xAxis
=
dataObj
.
trialTime
;
let
yAxis
=
dataObj
.
trialId
;
let
option
=
{
tooltip
:
{
trigger
:
'
axis
'
,
axisPointer
:
{
type
:
'
shadow
'
}
},
// title: {
// left: 'center',
// text: 'Trial Duration',
// textStyle: {
// fontSize: 18,
// color: '#333'
// }
// },
grid
:
{
bottom
:
'
3%
'
,
containLabel
:
true
,
left
:
'
1%
'
,
right
:
'
4%
'
},
dataZoom
:
[{
type
:
'
slider
'
,
name
:
'
trial
'
,
filterMode
:
'
filter
'
,
yAxisIndex
:
0
,
orient
:
'
vertical
'
},
{
type
:
'
slider
'
,
name
:
'
trial
'
,
filterMode
:
'
filter
'
,
xAxisIndex
:
0
}],
xAxis
:
{
name
:
'
Time
'
,
type
:
'
value
'
,
},
yAxis
:
{
name
:
'
Trial
'
,
type
:
'
category
'
,
data
:
yAxis
},
series
:
[{
type
:
'
bar
'
,
data
:
xAxis
}]
};
return
option
;
}
drawRunGraph
=
()
=>
{
axios
(
`
${
MANAGER_IP
}
/trial-jobs`
,
{
method
:
'
GET
'
})
.
then
(
res
=>
{
if
(
res
.
status
===
200
)
{
const
trialJobs
=
res
.
data
;
const
trialId
:
Array
<
string
>
=
[];
const
trialTime
:
Array
<
number
>
=
[];
const
trialRun
:
Array
<
Runtrial
>
=
[];
Object
.
keys
(
trialJobs
).
map
(
item
=>
{
if
(
trialJobs
[
item
].
status
!==
'
WAITING
'
)
{
let
duration
:
number
=
0
;
const
end
=
trialJobs
[
item
].
endTime
;
const
start
=
trialJobs
[
item
].
startTime
;
if
(
start
&&
end
)
{
duration
=
(
end
-
start
)
/
1000
;
}
else
{
duration
=
(
new
Date
().
getTime
()
-
start
)
/
1000
;
}
trialId
.
push
(
trialJobs
[
item
].
id
);
trialTime
.
push
(
duration
);
}
});
trialRun
.
push
({
trialId
:
trialId
,
trialTime
:
trialTime
});
if
(
this
.
_isMounted
&&
res
.
status
===
200
)
{
this
.
setState
({
durationSource
:
this
.
getOption
(
trialRun
[
0
])
});
}
}
});
}
componentDidMount
()
{
this
.
_isMounted
=
true
;
this
.
drawRunGraph
();
Duration
.
intervalDuration
=
window
.
setInterval
(
this
.
drawRunGraph
,
10000
);
}
componentWillUnmount
()
{
this
.
_isMounted
=
false
;
window
.
clearInterval
(
Duration
.
intervalDuration
);
}
render
()
{
const
{
durationSource
}
=
this
.
state
;
return
(
<
div
>
<
ReactEcharts
option
=
{
durationSource
}
style
=
{
{
width
:
'
100%
'
,
height
:
412
,
margin
:
'
0 auto
'
}
}
theme
=
"my_theme"
/>
</
div
>
);
}
}
export
default
Duration
;
\ No newline at end of file
src/webui/src/components/Para.tsx
→
src/webui/src/components/
trial-detail/
Para.tsx
View file @
744885eb
import
*
as
React
from
'
react
'
;
import
axios
from
'
axios
'
;
import
{
MANAGER_IP
}
from
'
../const
'
;
import
{
MANAGER_IP
}
from
'
../
../static/
const
'
;
import
ReactEcharts
from
'
echarts-for-react
'
;
import
{
Select
,
Button
,
message
}
from
'
antd
'
;
import
{
Row
,
Col
,
Select
,
Button
,
message
}
from
'
antd
'
;
import
{
HoverName
,
ParaObj
,
VisualMapValue
,
Dimobj
}
from
'
../../static/interface
'
;
const
Option
=
Select
.
Option
;
require
(
'
echarts/lib/chart/parallel
'
);
require
(
'
echarts/lib/component/tooltip
'
);
require
(
'
echarts/lib/component/title
'
);
require
(
'
echarts/lib/component/visualMap
'
);
require
(
'
../style/para.css
'
);
interface
Dimobj
{
dim
:
number
;
name
:
string
;
max
?:
number
;
min
?:
number
;
type
?:
string
;
data
?:
string
[];
}
interface
HoverName
{
name
:
string
;
}
interface
ParaObj
{
data
:
number
[][];
parallelAxis
:
Array
<
Dimobj
>
;
}
interface
VisualMapValue
{
maxAccuracy
:
number
;
minAccuracy
:
number
;
}
require
(
'
../../static/style/para.scss
'
);
require
(
'
../../static/style/button.scss
'
);
interface
ParaState
{
option
:
object
;
...
...
@@ -50,7 +29,7 @@ message.config({
class
Para
extends
React
.
Component
<
{},
ParaState
>
{
publ
ic
intervalIDPara
=
4
;
stat
ic
intervalIDPara
=
4
;
public
_isMounted
=
false
;
constructor
(
props
:
{})
{
...
...
@@ -224,7 +203,7 @@ class Para extends React.Component<{}, ParaState> {
// get percent value number
percentNum
=
(
value
:
string
)
=>
{
window
.
clearInterval
(
this
.
intervalIDPara
);
window
.
clearInterval
(
Para
.
intervalIDPara
);
let
vals
=
parseFloat
(
value
);
if
(
this
.
_isMounted
)
{
this
.
setState
(()
=>
({
...
...
@@ -232,7 +211,7 @@ class Para extends React.Component<{}, ParaState> {
}));
}
this
.
hyperParaPic
();
this
.
intervalIDPara
=
window
.
setInterval
(
this
.
hyperParaPic
,
10000
);
Para
.
intervalIDPara
=
window
.
setInterval
(
this
.
hyperParaPic
,
10000
);
}
// deal with response data into pic data
...
...
@@ -321,13 +300,12 @@ class Para extends React.Component<{}, ParaState> {
swapBtn
=
()
=>
{
window
.
clearInterval
(
this
.
intervalIDPara
);
window
.
clearInterval
(
Para
.
intervalIDPara
);
this
.
hyperParaPic
();
this
.
intervalIDPara
=
window
.
setInterval
(
this
.
hyperParaPic
,
10000
);
Para
.
intervalIDPara
=
window
.
setInterval
(
this
.
hyperParaPic
,
10000
);
}
sortDimY
=
(
a
:
Dimobj
,
b
:
Dimobj
)
=>
{
return
a
.
dim
-
b
.
dim
;
}
...
...
@@ -389,33 +367,32 @@ class Para extends React.Component<{}, ParaState> {
this
.
_isMounted
=
true
;
// default draw all data pic
this
.
hyperParaPic
();
this
.
intervalIDPara
=
window
.
setInterval
(
this
.
hyperParaPic
,
10000
);
Para
.
intervalIDPara
=
window
.
setInterval
(
this
.
hyperParaPic
,
10000
);
}
componentWillUnmount
()
{
this
.
_isMounted
=
false
;
window
.
clearInterval
(
this
.
intervalIDPara
);
window
.
clearInterval
(
Para
.
intervalIDPara
);
}
render
()
{
const
{
option
,
paraNodata
,
dimName
}
=
this
.
state
;
const
chartMulineStyle
=
{
width
:
'
100%
'
,
height
:
600
,
height
:
392
,
margin
:
'
0 auto
'
,
padding
:
15
padding
:
'
0 15 10 15
'
};
return
(
<
div
className
=
"para"
>
<
div
className
=
"paraCon"
>
<
div
className
=
"paraTitle"
>
<
div
className
=
"paraLeft"
>
Hyper Parameter
</
div
>
<
div
className
=
"
paraRight
"
>
<
Row
className
=
"para
meter
"
>
<
Row
>
<
Col
span
=
{
6
}
/
>
<
Col
span
=
{
18
}
>
<
Row
className
=
"
meline
"
>
<
span
>
top
</
span
>
<
Select
className
=
"parapercent"
style
=
{
{
width
:
'
15%
'
}
}
style
=
{
{
width
:
'
20%
'
,
marginRight
:
10
}
}
placeholder
=
"100%"
optionFilterProp
=
"children"
onSelect
=
{
this
.
percentNum
}
...
...
@@ -426,7 +403,7 @@ class Para extends React.Component<{}, ParaState> {
<
Option
value
=
"1"
>
100%
</
Option
>
</
Select
>
<
Select
style
=
{
{
width
:
'
5
0%
'
}
}
style
=
{
{
width
:
'
6
0%
'
}
}
mode
=
"multiple"
placeholder
=
"Please select two items to swap"
onChange
=
{
this
.
getSwapArr
}
...
...
@@ -447,20 +424,19 @@ class Para extends React.Component<{}, ParaState> {
>
Confirm
</
Button
>
</
div
>
</
div
>
<
div
className
=
"paraGra"
>
<
ReactEcharts
className
=
"testt"
option
=
{
option
}
style
=
{
chartMulineStyle
}
// lazyUpdate={true}
notMerge
=
{
true
}
// update now
/>
<
div
className
=
"paraNodata"
>
{
paraNodata
}
</
div
>
</
div
>
</
div
>
</
div
>
</
Row
>
</
Col
>
</
Row
>
<
Row
className
=
"searcHyper"
>
<
ReactEcharts
option
=
{
option
}
style
=
{
chartMulineStyle
}
// lazyUpdate={true}
notMerge
=
{
true
}
// update now
/>
<
div
className
=
"noneData"
>
{
paraNodata
}
</
div
>
</
Row
>
</
Row
>
);
}
}
...
...
src/webui/src/components/
T
rial
Status
.tsx
→
src/webui/src/components/
t
rial
-detail/TableList
.tsx
View file @
744885eb
import
*
as
React
from
'
react
'
;
import
{
browserHistory
}
from
'
react-router
'
;
import
axios
from
'
axios
'
;
import
{
Table
,
Button
,
Popconfirm
,
message
,
Modal
}
from
'
antd
'
;
import
{
MANAGER_IP
,
trialJobStatus
}
from
'
../const
'
;
import
JSONTree
from
'
react-json-tree
'
;
import
{
browserHistory
}
from
'
react-router
'
;
import
ReactEcharts
from
'
echarts-for-react
'
;
import
{
Row
,
Table
,
Button
,
Popconfirm
,
Modal
,
message
}
from
'
antd
'
;
import
{
MANAGER_IP
,
trialJobStatus
}
from
'
../../static/const
'
;
import
{
convertDuration
}
from
'
../../static/function
'
;
import
{
TableObj
,
TrialJob
}
from
'
../../static/interface
'
;
import
Title1
from
'
../overview/Title1
'
;
require
(
'
../../static/style/tableStatus.css
'
);
require
(
'
../../static/style/logPath.scss
'
);
require
(
'
../../static/style/table.scss
'
);
require
(
'
../../static/style/button.scss
'
);
const
echarts
=
require
(
'
echarts/lib/echarts
'
);
require
(
'
echarts/lib/chart/bar
'
);
require
(
'
echarts/lib/chart/line
'
);
require
(
'
echarts/lib/chart/scatter
'
);
require
(
'
echarts/lib/component/tooltip
'
);
require
(
'
echarts/lib/component/title
'
);
require
(
'
../style/trialStatus.css
'
);
require
(
'
../style/logPath.css
'
);
echarts
.
registerTheme
(
'
my_theme
'
,
{
color
:
'
#3c8dbc
'
});
interface
ErrorPara
{
error
?:
string
;
}
interface
DescObj
{
parameters
:
ErrorPara
;
logPath
?:
string
;
isLink
?:
boolean
;
}
interface
TableObj
{
key
:
number
;
id
:
string
;
duration
?:
number
;
start
:
string
;
status
:
string
;
description
:
DescObj
;
end
?:
string
;
acc
?:
number
;
}
interface
Runtrial
{
trialId
:
Array
<
string
>
;
trialTime
:
Array
<
number
>
;
}
interface
TrialJob
{
text
:
string
;
value
:
string
;
interface
TableListProps
{
tableSource
:
Array
<
TableObj
>
;
updateList
:
Function
;
}
interface
TabState
{
tableData
:
Array
<
TableObj
>
;
downhref
:
string
;
option
:
object
;
trialJobs
:
object
;
interface
TableListState
{
intermediateOption
:
object
;
modalVisible
:
boolean
;
disTensorButton
:
boolean
;
}
class
T
rialStatus
extends
React
.
Component
<
{},
Tab
State
>
{
class
T
ableList
extends
React
.
Component
<
TableListProps
,
TableList
State
>
{
public
intervalID
=
0
;
public
intervalIDS
=
1
;
public
_isMounted
=
false
;
constructor
(
props
:
{})
{
constructor
(
props
:
TableListProps
)
{
super
(
props
);
this
.
state
=
{
tableData
:
[{
key
:
0
,
id
:
''
,
duration
:
0
,
start
:
''
,
end
:
''
,
status
:
''
,
acc
:
0
,
description
:
{
parameters
:
{}
}
}],
downhref
:
'
javascript:;
'
,
option
:
{},
intermediateOption
:
{},
trialJobs
:
{},
modalVisible
:
false
,
disTensorButton
:
false
modalVisible
:
false
};
}
...
...
@@ -100,10 +53,11 @@ class TrialStatus extends React.Component<{}, TabState> {
Object
.
keys
(
res
.
data
).
map
(
item
=>
{
intermediateArr
.
push
(
parseFloat
(
res
.
data
[
item
].
data
));
});
const
intermediate
=
this
.
intermediateGraphOption
(
intermediateArr
,
id
);
if
(
this
.
_isMounted
)
{
this
.
setState
({
intermediateOption
:
this
.
intermediate
GraphOption
(
intermediateArr
,
id
)
});
this
.
setState
(()
=>
({
intermediateOption
:
intermediate
})
)
;
}
}
});
...
...
@@ -122,170 +76,44 @@ class TrialStatus extends React.Component<{}, TabState> {
}
}
getOption
=
(
dataObj
:
Runtrial
)
=>
{
let
xAxis
=
dataObj
.
trialTime
;
let
yAxis
=
dataObj
.
trialId
;
let
option
=
{
tooltip
:
{
trigger
:
'
axis
'
,
axisPointer
:
{
type
:
'
shadow
'
}
},
intermediateGraphOption
=
(
intermediateArr
:
number
[],
id
:
string
)
=>
{
const
sequence
:
number
[]
=
[];
const
lengthInter
=
intermediateArr
.
length
;
for
(
let
i
=
1
;
i
<=
lengthInter
;
i
++
)
{
sequence
.
push
(
i
);
}
return
{
title
:
{
text
:
id
,
left
:
'
center
'
,
text
:
'
Trial Duration
'
,
textStyle
:
{
fontSize
:
1
8
,
color
:
'
#333
'
fontSize
:
1
6
,
color
:
'
#333
'
,
}
},
grid
:
{
bottom
:
'
3%
'
,
containLabel
:
true
,
left
:
'
1%
'
,
right
:
'
4%
'
tooltip
:
{
trigger
:
'
item
'
},
dataZoom
:
[{
type
:
'
slider
'
,
name
:
'
trial
'
,
filterMode
:
'
filter
'
,
yAxisIndex
:
0
,
orient
:
'
vertical
'
},
{
type
:
'
slider
'
,
name
:
'
trial
'
,
filterMode
:
'
filter
'
,
xAxisIndex
:
0
}],
xAxis
:
{
name
:
'
T
ime
'
,
type
:
'
value
'
,
name
:
'
T
rial
'
,
data
:
sequence
},
yAxis
:
{
name
:
'
Trial
'
,
type
:
'
category
'
,
data
:
yAxis
name
:
'
Accuracy
'
,
type
:
'
value
'
,
data
:
intermediateArr
},
series
:
[{
type
:
'
bar
'
,
data
:
xAxis
symbolSize
:
6
,
type
:
'
scatter
'
,
data
:
intermediateArr
}]
};
return
option
;
}
drawRunGraph
=
()
=>
{
axios
(
`
${
MANAGER_IP
}
/trial-jobs`
,
{
method
:
'
GET
'
})
.
then
(
res
=>
{
if
(
res
.
status
===
200
)
{
const
trialJobs
=
res
.
data
;
const
trialId
:
Array
<
string
>
=
[];
const
trialTime
:
Array
<
number
>
=
[];
const
trialRun
:
Array
<
Runtrial
>
=
[];
Object
.
keys
(
trialJobs
).
map
(
item
=>
{
if
(
trialJobs
[
item
].
status
!==
'
WAITING
'
)
{
let
duration
:
number
=
0
;
const
end
=
trialJobs
[
item
].
endTime
;
const
start
=
trialJobs
[
item
].
startTime
;
if
(
start
&&
end
)
{
duration
=
(
end
-
start
)
/
1000
;
}
else
{
duration
=
(
new
Date
().
getTime
()
-
start
)
/
1000
;
}
trialId
.
push
(
trialJobs
[
item
].
id
);
trialTime
.
push
(
duration
);
}
});
trialRun
.
push
({
trialId
:
trialId
,
trialTime
:
trialTime
});
if
(
this
.
_isMounted
&&
res
.
status
===
200
)
{
this
.
setState
({
option
:
this
.
getOption
(
trialRun
[
0
])
});
}
}
});
}
drawTable
=
()
=>
{
axios
(
`
${
MANAGER_IP
}
/trial-jobs`
,
{
method
:
'
GET
'
})
.
then
(
res
=>
{
if
(
res
.
status
===
200
)
{
const
trialJobs
=
res
.
data
;
const
trialTable
:
Array
<
TableObj
>
=
[];
Object
.
keys
(
trialJobs
).
map
(
item
=>
{
// only succeeded trials have finalMetricData
let
desc
:
DescObj
=
{
parameters
:
{}
};
let
acc
=
0
;
let
duration
=
0
;
const
id
=
trialJobs
[
item
].
id
!==
undefined
?
trialJobs
[
item
].
id
:
''
;
const
status
=
trialJobs
[
item
].
status
!==
undefined
?
trialJobs
[
item
].
status
:
''
;
const
startTime
=
trialJobs
[
item
].
startTime
!==
undefined
?
new
Date
(
trialJobs
[
item
].
startTime
).
toLocaleString
(
'
en-US
'
)
:
''
;
const
endTime
=
trialJobs
[
item
].
endTime
!==
undefined
?
new
Date
(
trialJobs
[
item
].
endTime
).
toLocaleString
(
'
en-US
'
)
:
''
;
if
(
trialJobs
[
item
].
hyperParameters
!==
undefined
)
{
desc
.
parameters
=
JSON
.
parse
(
trialJobs
[
item
].
hyperParameters
).
parameters
;
}
else
{
desc
.
parameters
=
{
error
:
'
This trial
\'
s parameters are not available.
'
};
}
if
(
trialJobs
[
item
].
logPath
!==
undefined
)
{
desc
.
logPath
=
trialJobs
[
item
].
logPath
;
const
isHyperLink
=
/^http/gi
.
test
(
trialJobs
[
item
].
logPath
);
if
(
isHyperLink
)
{
desc
.
isLink
=
true
;
}
}
if
(
trialJobs
[
item
].
finalMetricData
!==
undefined
)
{
acc
=
parseFloat
(
trialJobs
[
item
].
finalMetricData
.
data
);
}
if
(
startTime
!==
''
&&
endTime
!==
''
)
{
duration
=
(
trialJobs
[
item
].
endTime
-
trialJobs
[
item
].
startTime
)
/
1000
;
}
else
if
(
startTime
!==
''
&&
endTime
===
''
)
{
duration
=
(
new
Date
().
getTime
()
-
trialJobs
[
item
].
startTime
)
/
1000
;
}
else
{
duration
=
0
;
}
trialTable
.
push
({
key
:
trialTable
.
length
,
id
:
id
,
status
:
status
,
start
:
startTime
,
end
:
endTime
,
duration
:
duration
,
acc
:
acc
,
description
:
desc
});
});
if
(
this
.
_isMounted
)
{
this
.
setState
(()
=>
({
tableData
:
trialTable
}));
}
}
});
}
// kill job
killJob
=
(
key
:
number
,
id
:
string
,
status
:
string
)
=>
{
const
{
updateList
}
=
this
.
props
;
axios
(
`
${
MANAGER_IP
}
/trial-jobs/
${
id
}
`
,
{
method
:
'
DELETE
'
,
headers
:
{
...
...
@@ -296,7 +124,7 @@ class TrialStatus extends React.Component<{}, TabState> {
if
(
res
.
status
===
200
)
{
message
.
success
(
'
Cancel the job successfully
'
);
// render the table
this
.
drawTable
();
updateList
();
}
else
{
message
.
error
(
'
fail to cancel the job
'
);
}
...
...
@@ -319,98 +147,17 @@ class TrialStatus extends React.Component<{}, TabState> {
browserHistory
.
push
(
path
);
}
intermediateGraphOption
=
(
intermediateArr
:
number
[],
id
:
string
)
=>
{
const
sequence
:
number
[]
=
[];
const
lengthInter
=
intermediateArr
.
length
;
for
(
let
i
=
1
;
i
<=
lengthInter
;
i
++
)
{
sequence
.
push
(
i
);
}
return
{
title
:
{
text
:
id
,
left
:
'
center
'
,
textStyle
:
{
fontSize
:
16
,
color
:
'
#333
'
,
}
},
tooltip
:
{
trigger
:
'
item
'
},
xAxis
:
{
name
:
'
Trial
'
,
data
:
sequence
},
yAxis
:
{
name
:
'
Accuracy
'
,
type
:
'
value
'
,
data
:
intermediateArr
},
series
:
[{
symbolSize
:
6
,
type
:
'
scatter
'
,
data
:
intermediateArr
}]
};
}
convertTime
=
(
num
:
number
)
=>
{
const
hour
=
Math
.
floor
(
num
/
3600
);
const
min
=
Math
.
floor
(
num
/
60
%
60
);
const
second
=
Math
.
floor
(
num
%
60
);
const
result
=
hour
>
0
?
`
${
hour
}
h
${
min
}
min
${
second
}
s`
:
`
${
min
}
min
${
second
}
s`
;
if
(
hour
<=
0
&&
min
===
0
&&
second
!==
0
)
{
return
`
${
second
}
s`
;
}
else
if
(
hour
===
0
&&
min
!==
0
&&
second
===
0
)
{
return
`
${
min
}
min`
;
}
else
if
(
hour
===
0
&&
min
!==
0
&&
second
!==
0
)
{
return
`
${
min
}
min
${
second
}
s`
;
}
else
{
return
result
;
}
}
// experiment mode is pai, display tensorboard button
disTensorBoard
=
()
=>
{
axios
(
`
${
MANAGER_IP
}
/experiment`
,
{
method
:
'
GET
'
})
.
then
(
res
=>
{
if
(
res
.
status
===
200
)
{
const
experimentObj
=
res
.
data
;
const
trainPlatform
=
experimentObj
.
params
.
trainingServicePlatform
;
if
(
trainPlatform
&&
trainPlatform
===
'
pai
'
)
{
if
(
this
.
_isMounted
)
{
this
.
setState
(()
=>
({
disTensorButton
:
true
}));
}
}
}
});
}
componentDidMount
()
{
this
.
_isMounted
=
true
;
// the init of running chart
this
.
drawRunGraph
();
// the init of trials status in the table
this
.
drawTable
();
this
.
disTensorBoard
();
this
.
intervalID
=
window
.
setInterval
(
this
.
drawRunGraph
,
10000
);
this
.
intervalIDS
=
window
.
setInterval
(
this
.
drawTable
,
10000
);
}
componentWillUnmount
()
{
this
.
_isMounted
=
false
;
window
.
clearInterval
(
this
.
intervalID
);
window
.
clearInterval
(
this
.
intervalIDS
);
}
render
()
{
const
{
intermediateOption
,
modalVisible
,
option
,
tableData
,
disTensorButton
}
=
this
.
state
;
const
{
tableSource
}
=
this
.
props
;
const
{
intermediateOption
,
modalVisible
}
=
this
.
state
;
let
bgColor
=
''
;
const
trialJob
:
Array
<
TrialJob
>
=
[];
trialJobStatus
.
map
(
item
=>
{
...
...
@@ -420,10 +167,22 @@ class TrialStatus extends React.Component<{}, TabState> {
});
});
const
columns
=
[{
title
:
'
Number
'
,
dataIndex
:
'
sequenceId
'
,
key
:
'
sequenceId
'
,
width
:
120
,
className
:
'
tableHead
'
,
sorter
:
(
a
:
TableObj
,
b
:
TableObj
)
=>
(
a
.
sequenceId
as
number
)
-
(
b
.
sequenceId
as
number
),
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
return
(
<
span
>
#
{
record
.
sequenceId
}
</
span
>
);
}
},
{
title
:
'
Id
'
,
dataIndex
:
'
id
'
,
key
:
'
id
'
,
width
:
'
10%
'
,
width
:
150
,
className
:
'
tableHead
'
,
// the sort of string
sorter
:
(
a
:
TableObj
,
b
:
TableObj
):
number
=>
a
.
id
.
localeCompare
(
b
.
id
)
...
...
@@ -431,37 +190,26 @@ class TrialStatus extends React.Component<{}, TabState> {
title
:
'
Duration
'
,
dataIndex
:
'
duration
'
,
key
:
'
duration
'
,
width
:
'
10%
'
,
width
:
150
,
// the sort of number
sorter
:
(
a
:
TableObj
,
b
:
TableObj
)
=>
(
a
.
duration
as
number
)
-
(
b
.
duration
as
number
),
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
const
bg
=
record
.
color
;
let
duration
;
if
(
record
.
duration
!==
undefined
&&
record
.
duration
>
0
)
{
duration
=
this
.
convert
Time
(
record
.
duration
);
duration
=
convert
Duration
(
record
.
duration
);
}
else
{
duration
=
0
;
}
return
(
<
span
>
{
duration
}
</
span
>
<
div
style
=
{
{
background
:
bg
}
}
>
{
duration
}
</
div
>
);
},
},
{
title
:
'
Start
'
,
dataIndex
:
'
start
'
,
key
:
'
start
'
,
width
:
'
15%
'
,
sorter
:
(
a
:
TableObj
,
b
:
TableObj
):
number
=>
(
Date
.
parse
(
a
.
start
)
-
Date
.
parse
(
b
.
start
))
},
{
title
:
'
End
'
,
dataIndex
:
'
end
'
,
key
:
'
end
'
,
width
:
'
15%
'
,
sorter
:
(
a
:
TableObj
,
b
:
TableObj
):
number
=>
(
a
.
end
as
string
).
localeCompare
(
b
.
end
as
string
)
},
{
title
:
'
Status
'
,
dataIndex
:
'
status
'
,
key
:
'
status
'
,
width
:
'
10%
'
,
width
:
150
,
className
:
'
tableStatus
'
,
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
bgColor
=
record
.
status
;
...
...
@@ -473,16 +221,29 @@ class TrialStatus extends React.Component<{}, TabState> {
onFilter
:
(
value
:
string
,
record
:
TableObj
)
=>
record
.
status
.
indexOf
(
value
)
===
0
,
sorter
:
(
a
:
TableObj
,
b
:
TableObj
):
number
=>
a
.
status
.
localeCompare
(
b
.
status
)
},
{
title
:
'
Loss/Accuracy
'
,
title
:
'
Default Metric
'
,
dataIndex
:
'
acc
'
,
key
:
'
acc
'
,
width
:
'
10%
'
,
sorter
:
(
a
:
TableObj
,
b
:
TableObj
)
=>
(
a
.
acc
as
number
)
-
(
b
.
acc
as
number
)
width
:
200
,
sorter
:
(
a
:
TableObj
,
b
:
TableObj
)
=>
(
a
.
acc
as
number
)
-
(
b
.
acc
as
number
),
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
return
(
<
div
>
{
record
.
acc
?
record
.
acc
.
toFixed
(
6
)
:
record
.
acc
}
</
div
>
);
}
},
{
title
:
'
Operation
'
,
dataIndex
:
'
operation
'
,
key
:
'
operation
'
,
width
:
'
10%
'
,
width
:
90
,
render
:
(
text
:
string
,
record
:
TableObj
)
=>
{
let
trialStatus
=
record
.
status
;
let
flagKill
=
false
;
...
...
@@ -513,7 +274,7 @@ class TrialStatus extends React.Component<{}, TabState> {
</
Button
>
)
);
}
}
,
},
{
title
:
'
Tensor
'
,
dataIndex
:
'
tensor
'
,
...
...
@@ -524,7 +285,6 @@ class TrialStatus extends React.Component<{}, TabState> {
<
Button
type
=
"primary"
className
=
"tableButton"
disabled
=
{
disTensorButton
}
onClick
=
{
this
.
getTensorpage
.
bind
(
this
,
record
.
id
)
}
>
TensorBoard
...
...
@@ -533,6 +293,7 @@ class TrialStatus extends React.Component<{}, TabState> {
},
}
];
const
openRow
=
(
record
:
TableObj
)
=>
{
let
isHasParameters
=
true
;
if
(
record
.
description
.
parameters
.
error
)
{
...
...
@@ -547,8 +308,8 @@ class TrialStatus extends React.Component<{}, TabState> {
isLogLink
=
true
;
}
return
(
<
pre
className
=
"hyperpar"
>
{
<
pre
id
=
"allList"
className
=
"hyperpar"
>
{
isHasParameters
?
<
JSONTree
...
...
@@ -588,40 +349,37 @@ class TrialStatus extends React.Component<{}, TabState> {
};
return
(
<
div
className
=
"hyper"
id
=
"tableCenter"
>
<
ReactEcharts
option
=
{
option
}
style
=
{
{
width
:
'
100%
'
,
height
:
600
,
marginBottom
:
15
}
}
theme
=
"my_theme"
/>
<
Table
columns
=
{
columns
}
expandedRowRender
=
{
openRow
}
dataSource
=
{
tableData
}
pagination
=
{
{
pageSize
:
10
}
}
className
=
"tables"
bordered
=
{
true
}
/>
<
Modal
title
=
"Intermediate Result"
visible
=
{
modalVisible
}
onCancel
=
{
this
.
hideIntermediateModal
}
footer
=
{
null
}
destroyOnClose
=
{
true
}
width
=
"80%"
>
<
ReactEcharts
option
=
{
intermediateOption
}
style
=
{
{
width
:
'
100%
'
,
height
:
0.7
*
window
.
innerHeight
}
}
theme
=
"my_theme"
<
Row
className
=
"tableList"
>
<
Row
><
Title1
text
=
"All Trials"
icon
=
"6.png"
/></
Row
>
<
div
id
=
"tableList"
>
<
Table
columns
=
{
columns
}
expandedRowRender
=
{
openRow
}
dataSource
=
{
tableSource
}
className
=
"commonTableStyle"
pagination
=
{
{
pageSize
:
10
}
}
/>
</
Modal
>
</
div
>
<
Modal
title
=
"Intermediate Result"
visible
=
{
modalVisible
}
onCancel
=
{
this
.
hideIntermediateModal
}
footer
=
{
null
}
destroyOnClose
=
{
true
}
width
=
"80%"
>
<
ReactEcharts
option
=
{
intermediateOption
}
style
=
{
{
width
:
'
100%
'
,
height
:
0.7
*
window
.
innerHeight
}
}
theme
=
"my_theme"
/>
</
Modal
>
</
div
>
</
Row
>
);
}
}
export
default
TrialStatus
;
\ No newline at end of file
export
default
TableList
;
\ No newline at end of file
src/webui/src/index.css
View file @
744885eb
/* http://meyerweb.com/eric/tools/css/reset/
v4.0 | 20180602
License: none (public domain)
*/
html
,
body
,
div
,
span
,
applet
,
object
,
iframe
,
h1
,
h2
,
h3
,
h4
,
h5
,
h6
,
p
,
blockquote
,
pre
,
a
,
abbr
,
acronym
,
address
,
big
,
cite
,
code
,
del
,
dfn
,
em
,
img
,
ins
,
kbd
,
q
,
s
,
samp
,
small
,
strike
,
strong
,
sub
,
sup
,
tt
,
var
,
b
,
u
,
i
,
center
,
dl
,
dt
,
dd
,
ol
,
ul
,
li
,
fieldset
,
form
,
label
,
legend
,
table
,
caption
,
tbody
,
tfoot
,
thead
,
tr
,
th
,
td
,
article
,
aside
,
canvas
,
details
,
embed
,
figure
,
figcaption
,
footer
,
header
,
hgroup
,
main
,
menu
,
nav
,
output
,
ruby
,
section
,
summary
,
time
,
mark
,
audio
,
video
{
margin
:
0
;
padding
:
0
;
border
:
0
;
font-size
:
100%
;
font
:
inherit
;
}
/* HTML5 display-role reset for older browsers */
article
,
aside
,
details
,
figcaption
,
figure
,
footer
,
header
,
hgroup
,
main
,
menu
,
nav
,
section
{
display
:
block
;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[
hidden
]
{
display
:
none
;
}
body
{
margin
:
0
;
padding
:
0
;
font-family
:
sans-serif
;
line-height
:
1
;
}
ol
,
ul
{
list-style
:
none
;
}
blockquote
,
q
{
quotes
:
none
;
}
blockquote
:before
,
blockquote
:after
,
q
:before
,
q
:after
{
content
:
''
;
content
:
none
;
}
table
{
border-collapse
:
collapse
;
border-spacing
:
0
;
}
pre
{
overflow
:
hidden
;
}
@font-face
{
font-family
:
'Segoe'
;
src
:
url('./static/font/SegoePro-Regular.ttf')
;
}
src/webui/src/index.tsx
View file @
744885eb
...
...
@@ -4,24 +4,21 @@ import * as ReactDOM from 'react-dom';
import
App
from
'
./App
'
;
import
{
Router
,
Route
,
browserHistory
,
IndexRedirect
}
from
'
react-router
'
;
import
registerServiceWorker
from
'
./registerServiceWorker
'
;
import
Accuracy
from
'
./components/Accuracy
'
;
import
Para
from
'
./components/Para
'
;
import
TrialStatus
from
'
./components/TrialStatus
'
;
import
Tensor
from
'
./components/Tensor
'
;
import
Control
from
'
./components/Control
'
;
import
Sessionpro
from
'
./components/Sessionpro
'
;
import
Overview
from
'
./components/Overview
'
;
import
TrialsDetail
from
'
./components/TrialsDetail
'
;
// import TrialsDetail from './components/TrialsDetail';
import
'
./index.css
'
;
ReactDOM
.
render
(
<
Router
history
=
{
browserHistory
}
>
<
Route
path
=
"/"
component
=
{
App
}
>
<
IndexRedirect
to
=
"/oview"
/>
<
Route
path
=
"/oview"
component
=
{
Sessionpro
}
/>
<
Route
path
=
"/hyper"
component
=
{
Para
}
/>
<
Route
path
=
"/trastaus"
component
=
{
TrialStatus
}
/>
<
Route
path
=
"/oview"
component
=
{
Overview
}
/>
<
Route
path
=
"/detail"
component
=
{
TrialsDetail
}
/>
<
Route
path
=
"/tensor"
component
=
{
Tensor
}
/>
<
Route
path
=
"/control"
component
=
{
Control
}
/>
<
Route
path
=
"/all"
component
=
{
Accuracy
}
/>
</
Route
>
</
Router
>,
document
.
getElementById
(
'
root
'
)
as
HTMLElement
...
...
src/webui/src/const.ts
→
src/webui/src/
static/
const.ts
View file @
744885eb
...
...
@@ -13,4 +13,4 @@ export const CONTROLTYPE = [
'
TRIAL_CONCURRENCY
'
,
'
MAX_EXEC_DURATION
'
];
export
const
overviewItem
=
5
0
;
export
const
overviewItem
=
1
0
;
src/webui/src/static/font/SegoePro-Regular.ttf
0 → 100644
View file @
744885eb
File added
src/webui/src/static/function.ts
0 → 100644
View file @
744885eb
import
{
AccurPoint
}
from
'
./interface
'
;
export
const
convertTime
=
(
num
:
number
)
=>
{
if
(
num
%
3600
===
0
)
{
return
num
/
3600
+
'
h
'
;
}
else
{
const
hour
=
Math
.
floor
(
num
/
3600
);
const
min
=
Math
.
floor
(
num
/
60
%
60
);
return
hour
>
0
?
`
${
hour
}
h
${
min
}
min`
:
`
${
min
}
min`
;
}
};
// trial's duration, accurate to seconds for example 10min 30s
export
const
convertDuration
=
(
num
:
number
)
=>
{
const
hour
=
Math
.
floor
(
num
/
3600
);
const
min
=
Math
.
floor
(
num
/
60
%
60
);
const
second
=
Math
.
floor
(
num
%
60
);
const
result
=
hour
>
0
?
`
${
hour
}
h
${
min
}
min
${
second
}
s`
:
`
${
min
}
min
${
second
}
s`
;
if
(
hour
<=
0
&&
min
===
0
&&
second
!==
0
)
{
return
`
${
second
}
s`
;
}
else
if
(
hour
===
0
&&
min
!==
0
&&
second
===
0
)
{
return
`
${
min
}
min`
;
}
else
if
(
hour
===
0
&&
min
!==
0
&&
second
!==
0
)
{
return
`
${
min
}
min
${
second
}
s`
;
}
else
{
return
result
;
}
};
// ACCURACY point graph option format
export
const
getAccuracyData
=
(
dataObj
:
AccurPoint
)
=>
{
const
yAxis
=
dataObj
.
yAxis
;
const
xAxis
:
Array
<
number
>
=
[];
for
(
let
i
=
1
;
i
<=
yAxis
.
length
;
i
++
)
{
xAxis
.
push
(
i
);
}
return
{
tooltip
:
{
trigger
:
'
item
'
},
xAxis
:
{
name
:
'
Trial
'
,
type
:
'
category
'
,
data
:
xAxis
},
yAxis
:
{
name
:
'
Accuracy
'
,
type
:
'
value
'
,
data
:
yAxis
},
series
:
[{
symbolSize
:
6
,
type
:
'
scatter
'
,
data
:
yAxis
}]
};
};
\ No newline at end of file
src/webui/src/static/img/icon/1.png
0 → 100644
View file @
744885eb
626 Bytes
src/webui/src/static/img/icon/10.png
0 → 100644
View file @
744885eb
2.57 KB
src/webui/src/static/img/icon/11.png
0 → 100644
View file @
744885eb
1.01 KB
src/webui/src/static/img/icon/2.png
0 → 100644
View file @
744885eb
323 Bytes
src/webui/src/static/img/icon/3.png
0 → 100644
View file @
744885eb
324 Bytes
src/webui/src/static/img/icon/4.png
0 → 100644
View file @
744885eb
400 Bytes
src/webui/src/static/img/icon/5.png
0 → 100644
View file @
744885eb
647 Bytes
src/webui/src/static/img/icon/6.png
0 → 100644
View file @
744885eb
415 Bytes
Prev
1
2
3
4
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment