Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MindSpore
mindinsight
提交
4975cb72
M
mindinsight
项目概览
MindSpore
/
mindinsight
通知
8
Star
4
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
mindinsight
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
4975cb72
编写于
6月 12, 2020
作者:
M
mindspore-ci-bot
提交者:
Gitee
6月 12, 2020
浏览文件
操作
浏览文件
下载
差异文件
!259 UI supprot profiling feature (1st commit)
Merge pull request !259 from 潘慧/master_ph
上级
20c153de
d73c66bd
变更
6
显示空白变更内容
内联
并排
Showing
6 changed file
with
2832 addition
and
0 deletion
+2832
-0
mindinsight/ui/src/assets/images/collapse-left.svg
mindinsight/ui/src/assets/images/collapse-left.svg
+31
-0
mindinsight/ui/src/assets/images/collapse-right.svg
mindinsight/ui/src/assets/images/collapse-right.svg
+31
-0
mindinsight/ui/src/views/train-manage/operator.vue
mindinsight/ui/src/views/train-manage/operator.vue
+1164
-0
mindinsight/ui/src/views/train-manage/profiling-dashboard.vue
...insight/ui/src/views/train-manage/profiling-dashboard.vue
+738
-0
mindinsight/ui/src/views/train-manage/profiling.vue
mindinsight/ui/src/views/train-manage/profiling.vue
+208
-0
mindinsight/ui/src/views/train-manage/step-trace.vue
mindinsight/ui/src/views/train-manage/step-trace.vue
+660
-0
未找到文件。
mindinsight/ui/src/assets/images/collapse-left.svg
0 → 100644
浏览文件 @
4975cb72
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"32px"
height=
"96px"
viewBox=
"0 0 32 96"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 63.1 (92452) - https://sketch.com -->
<title>
编组 12
</title>
<desc>
Created with Sketch.
</desc>
<defs>
<filter
x=
"-166.7%"
y=
"-25.0%"
width=
"433.3%"
height=
"150.0%"
filterUnits=
"objectBoundingBox"
id=
"filter-1"
>
<feOffset
dx=
"0"
dy=
"0"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
></feOffset>
<feGaussianBlur
stdDeviation=
"5"
in=
"shadowOffsetOuter1"
result=
"shadowBlurOuter1"
></feGaussianBlur>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"
type=
"matrix"
in=
"shadowBlurOuter1"
result=
"shadowMatrixOuter1"
></feColorMatrix>
<feMerge>
<feMergeNode
in=
"shadowMatrixOuter1"
></feMergeNode>
<feMergeNode
in=
"SourceGraphic"
></feMergeNode>
</feMerge>
</filter>
<polygon
id=
"path-2"
points=
"0 0 8 0 4 5"
></polygon>
</defs>
<g
id=
"智能小助手"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"3"
transform=
"translate(-461.000000, -525.000000)"
>
<g
id=
"编组-12"
filter=
"url(#filter-1)"
transform=
"translate(471.000000, 533.000000)"
>
<path
d=
"M-1.50990331e-14,0 L9.11107923,10.6295924 C10.9752941,12.8045097 12,15.5745537 12,18.4390889 L12,61.5609111 C12,64.4254463 10.9752941,67.1954903 9.11107923,69.3704076 L-1.50990331e-14,80 L-1.50990331e-14,80 L-1.50990331e-14,0 Z"
id=
"矩形"
fill=
"#FFFFFF"
></path>
<g
id=
"icon-下展"
transform=
"translate(6.000000, 40.500000) rotate(90.000000) translate(-6.000000, -40.500000) translate(2.000000, 38.000000)"
>
<mask
id=
"mask-3"
fill=
"white"
>
<use
xlink:href=
"#path-2"
></use>
</mask>
<use
id=
"蒙版"
fill=
"#575D6C"
fill-rule=
"evenodd"
xlink:href=
"#path-2"
></use>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
mindinsight/ui/src/assets/images/collapse-right.svg
0 → 100644
浏览文件 @
4975cb72
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"32px"
height=
"96px"
viewBox=
"0 0 32 96"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 63.1 (92452) - https://sketch.com -->
<title>
编组 12
</title>
<desc>
Created with Sketch.
</desc>
<defs>
<filter
x=
"-166.7%"
y=
"-25.0%"
width=
"433.3%"
height=
"150.0%"
filterUnits=
"objectBoundingBox"
id=
"filter-1"
>
<feOffset
dx=
"0"
dy=
"0"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
></feOffset>
<feGaussianBlur
stdDeviation=
"5"
in=
"shadowOffsetOuter1"
result=
"shadowBlurOuter1"
></feGaussianBlur>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"
type=
"matrix"
in=
"shadowBlurOuter1"
result=
"shadowMatrixOuter1"
></feColorMatrix>
<feMerge>
<feMergeNode
in=
"shadowMatrixOuter1"
></feMergeNode>
<feMergeNode
in=
"SourceGraphic"
></feMergeNode>
</feMerge>
</filter>
<polygon
id=
"path-2"
points=
"0 0 8 0 4 5"
></polygon>
</defs>
<g
id=
"智能小助手"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"3"
transform=
"translate(-461.000000, -525.000000)"
>
<g
id=
"编组-12"
filter=
"url(#filter-1)"
transform=
"translate(471.000000, 533.000000)"
>
<path
d=
"M-1.50990331e-14,0 L9.11107923,10.6295924 C10.9752941,12.8045097 12,15.5745537 12,18.4390889 L12,61.5609111 C12,64.4254463 10.9752941,67.1954903 9.11107923,69.3704076 L-1.50990331e-14,80 L-1.50990331e-14,80 L-1.50990331e-14,0 Z"
id=
"矩形"
fill=
"#FFFFFF"
></path>
<g
id=
"icon-下展"
transform=
"translate(6.000000, 40.500000) rotate(-90.000000) translate(-6.000000, -40.500000) translate(2.000000, 38.000000)"
>
<mask
id=
"mask-3"
fill=
"white"
>
<use
xlink:href=
"#path-2"
></use>
</mask>
<use
id=
"蒙版"
fill=
"#575D6C"
fill-rule=
"evenodd"
xlink:href=
"#path-2"
></use>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
mindinsight/ui/src/views/train-manage/operator.vue
0 → 100644
浏览文件 @
4975cb72
<
template
>
<div
class=
"operator"
>
<div
class=
"operator-title"
>
{{
$t
(
'
profiling.operatorDetail
'
)
}}
</div>
<div
class=
"cl-profiler"
>
<el-tabs
v-model=
"apiType"
@
tab-click=
"tabChange"
>
<el-tab-pane
label=
"AI CORE"
name=
"core"
>
<div
class=
"cl-profiler-top"
v-if=
"coreCharts.data.length"
>
<div>
<span
class=
"profiler-title"
>
{{
$t
(
'
operator.operatorTypeStatistics
'
)
}}
</span>
<el-radio-group
class=
"chart-radio-group"
v-model=
"coreCharts.type"
@
change=
"coreChartChange"
fill=
"#00A5A7"
text-color=
"#FFFFFF"
size=
"small"
>
<el-radio-button
:label=
"0"
>
{{
$t
(
'
operator.pie
'
)
}}
</el-radio-button>
<el-radio-button
:label=
"1"
>
{{
$t
(
'
operator.bar
'
)
}}
</el-radio-button>
</el-radio-group>
</div>
<div
class=
"cl-profiler-echarts"
>
<div
id=
"core-echarts"
></div>
</div>
</div>
<div
class=
"cl-profiler-bottom"
v-if=
"coreCharts.data.length"
>
<span
class=
"profiler-title"
>
{{
$t
(
'
operator.operatorStatistics
'
)
}}
</span>
<div>
<el-radio-group
v-model=
"statisticType"
@
change=
"coreTableChange"
fill=
"#00A5A7"
text-color=
"#FFFFFF"
size=
"small"
>
<el-radio-button
:label=
"1"
>
{{
$t
(
'
operator.allOperator
'
)
}}
</el-radio-button>
<el-radio-button
:label=
"0"
>
{{
$t
(
'
operator.ClassificationOperator
'
)
}}
</el-radio-button>
</el-radio-group>
<div
class=
"cl-search-box"
>
<el-input
v-model=
"searchByTypeInput"
v-if=
"!statisticType"
:placeholder=
"$t('operator.searchByType')"
clearable
@
clear=
"searchOpCoreList()"
@
keyup.enter.native=
"searchOpCoreList()"
></el-input>
<el-input
v-model=
"searchByNameInput"
v-if=
"statisticType"
:placeholder=
"$t('operator.searchByName')"
clearable
@
clear=
"searchOpCoreList()"
@
keyup.enter.native=
"searchOpCoreList()"
></el-input>
</div>
</div>
<el-table
v-show=
"!statisticType && opTypeCol && opTypeCol.length"
:data=
"opTypeList"
@
expand-change=
"expandTypeItem"
stripe
height=
"calc(100% - 75px)"
width=
"100%"
>
<el-table-column
type=
"expand"
>
<template
slot-scope=
"props"
>
<div
class=
"expand-table"
>
<el-table
:data=
"props.row.opDetailList"
stripe
width=
"100%"
tooltip-effect=
"light"
@
cell-click=
"showInfoDetail"
@
sort-change=
"(...args)=>
{coreDetailSortChange(props.row, ...args)}">
<el-table-column
v-for=
"(ele, key) in props.row.opDetailCol"
:property=
"ele"
:key=
"key"
:sortable=
"ele === 'op_info' ? false : 'custom'"
:width=
"(ele==='execution_time'|| ele==='subgraph' ||
ele==='op_name'|| ele==='op_type')?'220':''"
show-overflow-tooltip
:label=
"ele"
>
</el-table-column>
</el-table>
<el-pagination
:current-page=
"props.row.opDetailPage.offset + 1"
:page-size=
"props.row.opDetailPage.limit"
@
current-change=
"(...args)=>
{opDetailPageChange(props.row, ...args)}"
layout="total, prev, pager, next, jumper"
:total="props.row.pageTotal">
</el-pagination>
<div
class=
"clear"
></div>
</div>
</
template
>
</el-table-column>
<el-table-column
v-for=
"(item, $index) in opTypeCol"
:property=
"item"
:key=
"$index"
sortable
:label=
"item"
>
</el-table-column>
</el-table>
<el-table
v-show=
"statisticType && opAllTypeList.opDetailCol && opAllTypeList.opDetailCol.length"
:data=
"opAllTypeList.opDetailList"
stripe
width=
"100%"
height=
"calc(100% - 114px)"
@
cell-click=
"showInfoDetail"
@
sort-change=
"(...args)=>{coreDetailSortChange(opAllTypeList, ...args)}"
tooltip-effect=
"light"
>
<el-table-column
v-for=
"(item, $index) in opAllTypeList.opDetailCol"
:property=
"item"
:key=
"$index"
:label=
"item"
:sortable=
"item === 'op_info' ? false : 'custom'"
:width=
"(item==='execution_time'|| item==='subgraph' ||
item==='op_name'|| item==='op_type')?'220':''"
show-overflow-tooltip
>
</el-table-column>
</el-table>
<el-pagination
v-show=
"statisticType"
v-if=
"opAllTypeList.opDetailList.length"
:current-page=
"opAllTypeList.opDetailPage.offset + 1"
:page-size=
"opAllTypeList.opDetailPage.limit"
@
current-change=
"(...args)=>{opDetailPageChange(opAllTypeList, ...args)}"
layout=
"total, prev, pager, next, jumper"
:total=
"opAllTypeList.pageTotal"
>
</el-pagination>
</div>
<div
class=
"image-noData"
v-if=
"initOver && coreCharts.data.length === 0"
>
<div>
<img
:src=
"require('@/assets/images/nodata.png')"
alt=
""
/>
</div>
<p>
{{ $t("public.noData") }}
</p>
</div>
</el-tab-pane>
<el-tab-pane
label=
"AI CPU"
class=
"cpu-tab"
name=
"cpu"
v-if=
"false"
>
<div
class=
"cl-profiler-top"
v-if=
"cpuCharts.data.length"
>
<div>
<span
class=
"profiler-title"
>
{{ $t('operator.operatorTypeStatistics') }}
</span>
</div>
<div
class=
"cl-profiler-echarts"
>
<div
class
id=
"cpu-echarts"
></div>
</div>
</div>
<div
class=
"cl-profiler-bottom"
v-if=
"cpuCharts.data.length"
>
<span
class=
"profiler-title"
>
{{ $t('operator.operatorStatistics') }}
</span>
<div
class=
"cl-search-box"
>
<el-input
v-model=
"searchByCPUNameInput"
:placeholder=
"$t('operator.searchByName')"
clearable
@
clear=
"searchOpCpuList()"
@
keyup.enter.native=
"searchOpCpuList()"
></el-input>
</div>
<el-table
v-show=
"opCpuList.opDetailCol && opCpuList.opDetailCol.length"
:data=
"opCpuList.opDetailList"
stripe
width=
"100%"
height=
"calc(100% - 82px)"
tooltip-effect=
"light"
@
sort-change=
"(...args)=>{cpuDetailSortChange(opCpuList, ...args)}"
>
<el-table-column
v-for=
"(item, $index) in opCpuList.opDetailCol"
:property=
"item"
:key=
"$index"
:label=
"item"
sortable=
"custom"
show-overflow-tooltip
>
</el-table-column>
</el-table>
<el-pagination
v-if=
"opCpuList.opDetailList.length"
:current-page=
"opCpuList.opDetailPage.offset + 1"
:page-size=
"opCpuList.opDetailPage.limit"
@
current-change=
"(...args)=>{opCpuPageChange(opCpuList, ...args)}"
layout=
"total, prev, pager, next, jumper"
:total=
"opCpuList.pageTotal"
>
</el-pagination>
</div>
<div
class=
"image-noData"
v-if=
"initOver && cpuCharts.data.length === 0"
>
<div>
<img
:src=
"require('@/assets/images/nodata.png')"
alt=
""
/>
</div>
<p>
{{$t("public.noData")}}
</p>
</div>
</el-tab-pane>
</el-tabs>
<el-dialog
:title=
"rowName"
:visible.sync=
"detailsDialogVisible"
width=
"50%"
:close-on-click-modal=
"false"
class=
"details-data-list"
>
<el-table
:data=
"detailsDataList"
row-key=
"id"
lazy
tooltip-effect=
"light"
:load=
"loadDataListChildren"
:tree-props=
"{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column
width=
"50"
/>
<el-table-column
prop=
"key"
width=
"180"
label=
"Key"
>
</el-table-column>
<el-table-column
prop=
"value"
show-overflow-tooltip
label=
"Value"
>
<
template
slot-scope=
"scope"
>
{{
scope
.
row
.
value
}}
</
template
>
</el-table-column>
</el-table>
</el-dialog>
</div>
</div>
</template>
<
script
>
import
echarts
from
'
echarts
'
;
import
requestService
from
'
../../services/request-service
'
;
import
CommonProperty
from
'
../../common/common-property
'
;
export
default
{
data
()
{
return
{
apiType
:
'
core
'
,
currentCard
:
''
,
cpuCharts
:
{
type
:
1
,
id
:
'
cpu-echarts
'
,
chartDom
:
null
,
data
:
[],
},
// ai cpu chart
coreCharts
:
{
type
:
0
,
id
:
'
core-echarts
'
,
chartDom
:
null
,
data
:
[],
},
// ai core chart
statisticType
:
0
,
// ai core table statistic type
searchByTypeInput
:
''
,
// search by ai core type name
searchByNameInput
:
''
,
// search by ai core detail name
searchByCPUNameInput
:
''
,
// search by ai cpu name
opTypeCol
:
[],
// table headers list of operator type
opTypeList
:
[],
// table list of operator type
opCpuList
:
{
opDetailCol
:
[],
opDetailList
:
[],
pageTotal
:
0
,
opDetailPage
:
{
offset
:
0
,
limit
:
20
,
},
op_filter_condition
:
{},
op_sort_condition
:
{
name
:
'
total_time
'
,
type
:
'
descending
'
,
},
},
// table data of operator cpu
opAllTypeList
:
{
opDetailCol
:
[],
opDetailList
:
[],
pageTotal
:
0
,
opDetailPage
:
{
offset
:
0
,
limit
:
8
,
},
op_filter_condition
:
{},
op_sort_condition
:
{},
},
// table data of all operator details
rowName
:
this
.
$t
(
'
dataTraceback.details
'
),
// dialog title
detailsDataList
:
[],
// dialog table data
detailsDialogVisible
:
false
,
// show dialog
profile_dir
:
''
,
// profile directory
train_id
:
''
,
// train id
op_filter_condition
:
{},
// operator type filter
op_sort_condition
:
{
name
:
'
execution_time
'
,
type
:
'
descending
'
,
},
// operator type filter
initOver
:
false
,
objectType
:
'
object
'
,
};
},
watch
:
{
'
$parent.curDashboardInfo
'
:
{
handler
(
newValue
,
oldValue
)
{
if
(
newValue
.
query
.
dir
&&
newValue
.
query
.
id
&&
newValue
.
curCardNum
)
{
this
.
profile_dir
=
newValue
.
query
.
dir
;
this
.
train_id
=
newValue
.
query
.
id
;
this
.
currentCard
=
newValue
.
curCardNum
;
this
.
init
();
}
},
deep
:
true
,
immediate
:
true
,
},
},
destroyed
()
{
// Remove the listener of window size change
window
.
removeEventListener
(
'
resize
'
,
this
.
resizeCallback
);
},
methods
:
{
init
()
{
this
.
getCoreTypeList
();
},
/**
* Current device change
*/
cardChange
()
{
if
(
this
.
apiType
===
'
core
'
)
{
this
.
statisticType
=
0
;
this
.
clearCoreData
();
this
.
getCoreTypeList
();
}
else
if
(
this
.
apiType
===
'
cpu
'
)
{
this
.
clearCpuData
();
this
.
getCpuList
(
true
);
}
},
/**
* clear cpu data
*/
clearCpuData
()
{
this
.
searchByCPUNameInput
=
''
;
this
.
opCpuList
=
{
opDetailCol
:
[],
opDetailList
:
[],
pageTotal
:
0
,
opDetailPage
:
{
offset
:
0
,
limit
:
20
,
},
op_filter_condition
:
{},
op_sort_condition
:
{
name
:
'
total_time
'
,
type
:
'
descending
'
,
},
};
},
/**
* clear core data
*/
clearCoreData
()
{
this
.
searchByTypeInput
=
''
;
this
.
searchByNameInput
=
''
;
this
.
op_filter_condition
=
{};
this
.
opTypeCol
=
[];
this
.
opTypeList
=
[];
this
.
opAllTypeList
=
{
opDetailCol
:
[],
opDetailList
:
[],
pageTotal
:
0
,
opDetailPage
:
{
offset
:
0
,
limit
:
8
,
},
op_filter_condition
:
{},
op_sort_condition
:
{},
};
},
/**
* get core list
*/
getCoreTypeList
()
{
const
params
=
{};
params
.
params
=
{
profile
:
this
.
profile_dir
,
train_id
:
this
.
train_id
,
};
params
.
body
=
{
op_type
:
'
aicore_type
'
,
device_id
:
this
.
currentCard
,
filter_condition
:
this
.
op_filter_condition
,
sort_condition
:
this
.
op_sort_condition
,
};
requestService
.
getProfilerOpData
(
params
)
.
then
((
res
)
=>
{
this
.
initOver
=
true
;
this
.
opTypeList
=
[];
if
(
res
&&
res
.
data
)
{
this
.
opTypeCol
=
res
.
data
.
col_name
;
if
(
res
.
data
.
object
)
{
res
.
data
.
object
.
forEach
((
k
)
=>
{
const
object
=
{
isExpanded
:
false
,
opDetailList
:
[],
opDetailCol
:
[],
opDetailPage
:
{
offset
:
0
,
limit
:
8
,
},
pageTotal
:
0
,
op_filter_condition
:
{
op_type
:
{
in
:
[
k
[
0
]],
},
},
op_sort_condition
:
{},
};
res
.
data
.
col_name
.
forEach
((
item
,
index
)
=>
{
object
[
item
]
=
k
[
index
];
});
this
.
opTypeList
.
push
(
object
);
});
if
(
!
this
.
coreCharts
.
device_id
||
this
.
coreCharts
.
device_id
!==
this
.
currentCard
)
{
this
.
coreCharts
.
device_id
=
this
.
currentCard
;
this
.
coreCharts
.
data
=
[];
res
.
data
.
object
.
forEach
((
k
)
=>
{
if
(
this
.
coreCharts
.
data
&&
this
.
coreCharts
.
data
.
length
<
19
)
{
this
.
coreCharts
.
data
.
push
({
name
:
k
[
0
],
value
:
k
[
1
],
percent
:
k
[
3
],
});
}
else
{
if
(
!
this
.
coreCharts
.
data
[
19
])
{
this
.
coreCharts
.
data
[
19
]
=
{
name
:
'
Other
'
,
value
:
0
,
percent
:
0
,
};
}
this
.
coreCharts
.
data
[
19
].
value
+=
k
[
1
];
this
.
coreCharts
.
data
[
19
].
percent
+=
k
[
3
];
}
});
this
.
setOption
(
this
.
coreCharts
);
}
}
}
})
.
catch
(()
=>
{
this
.
opTypeList
=
[];
this
.
initOver
=
true
;
});
},
/**
* get core detail list
* @param {Object} row type row
*/
getCoreDetailList
(
row
)
{
const
params
=
{};
params
.
params
=
{
profile
:
this
.
profile_dir
,
train_id
:
this
.
train_id
,
};
params
.
body
=
{
op_type
:
'
aicore_detail
'
,
device_id
:
this
.
currentCard
,
filter_condition
:
row
.
op_filter_condition
,
sort_condition
:
row
.
op_sort_condition
,
group_condition
:
row
.
opDetailPage
,
};
requestService
.
getProfilerOpData
(
params
)
.
then
((
res
)
=>
{
if
(
res
&&
res
.
data
)
{
this
.
formatterDetailData
(
row
,
res
.
data
);
}
})
.
catch
(()
=>
{});
},
/**
* get cpu list
*/
getCpuList
()
{
const
params
=
{};
params
.
params
=
{
profile
:
this
.
profile_dir
,
train_id
:
this
.
train_id
,
};
params
.
body
=
{
op_type
:
'
aicpu
'
,
device_id
:
this
.
currentCard
,
filter_condition
:
this
.
opCpuList
.
op_filter_condition
,
sort_condition
:
this
.
opCpuList
.
op_sort_condition
,
group_condition
:
this
.
opCpuList
.
opDetailPage
,
};
requestService
.
getProfilerOpData
(
params
)
.
then
((
res
)
=>
{
this
.
initOver
=
true
;
if
(
res
&&
res
.
data
)
{
if
(
res
.
data
.
object
)
{
if
(
!
this
.
cpuCharts
.
device_id
||
this
.
cpuCharts
.
device_id
!==
this
.
currentCard
)
{
this
.
cpuCharts
.
device_id
=
this
.
currentCard
;
this
.
cpuCharts
.
data
=
[];
res
.
data
.
object
.
forEach
((
k
)
=>
{
this
.
cpuCharts
.
data
.
push
({
name
:
k
[
0
],
op_name
:
k
[
1
],
value
:
k
[
2
],
});
});
this
.
setOption
(
this
.
cpuCharts
);
}
if
(
res
.
data
.
object
.
length
>
8
)
{
this
.
opCpuList
.
opDetailPage
.
limit
=
8
;
res
.
data
.
object
.
splice
(
8
);
}
this
.
formatterDetailData
(
this
.
opCpuList
,
res
.
data
);
}
}
})
.
catch
(()
=>
{
this
.
initOver
=
true
;
});
},
/**
* operator detail list page change
* @param {Object} row table cell
* @param {Number} pageIndex current page
*/
opDetailPageChange
(
row
,
pageIndex
)
{
row
.
opDetailPage
.
offset
=
pageIndex
-
1
;
this
.
getCoreDetailList
(
row
);
},
/**
* cpu list page change
* @param {Object} row table cell
* @param {Number} pageIndex current page
*/
opCpuPageChange
(
row
,
pageIndex
)
{
row
.
opDetailPage
.
offset
=
pageIndex
-
1
;
this
.
getCpuList
();
},
/**
* get core list by search
*/
searchOpCoreList
()
{
if
(
this
.
statisticType
)
{
this
.
opAllTypeList
.
op_filter_condition
=
{};
if
(
this
.
searchByNameInput
)
{
this
.
opAllTypeList
.
op_filter_condition
=
{
op_name
:
{
partial_match_str_in
:
[
this
.
searchByNameInput
]},
};
}
else
{
this
.
opAllTypeList
.
op_filter_condition
=
{};
}
this
.
getCoreDetailList
(
this
.
opAllTypeList
);
}
else
{
this
.
op_filter_condition
=
{};
if
(
this
.
searchByTypeInput
)
{
this
.
op_filter_condition
=
{
op_type
:
{
partial_match_str_in
:
[
this
.
searchByTypeInput
]},
};
}
else
{
this
.
op_filter_condition
=
{};
}
this
.
getCoreTypeList
();
}
},
/**
* get cpu list by search
*/
searchOpCpuList
()
{
this
.
opCpuList
.
op_filter_condition
=
{};
if
(
this
.
searchByCPUNameInput
)
{
this
.
opCpuList
.
op_filter_condition
=
{
op_name
:
{
partial_match_str_in
:
[
this
.
searchByCPUNameInput
]},
};
}
else
{
this
.
opCpuList
.
op_filter_condition
=
{};
}
this
.
getCpuList
();
},
/**
* core detail sort
* @param {Object} row table cell
* @param {Object} column table cell
*/
coreDetailSortChange
(
row
,
column
)
{
row
.
op_sort_condition
=
{
name
:
column
.
prop
,
type
:
column
.
order
,
};
row
.
opDetailPage
.
offset
=
0
;
this
.
getCoreDetailList
(
row
);
},
/**
* cpu detail sort
* @param {Object} row table cell
* @param {Object} column table cell
*/
cpuDetailSortChange
(
row
,
column
)
{
row
.
op_sort_condition
=
{
name
:
column
.
prop
,
type
:
column
.
order
,
};
row
.
opDetailPage
.
offset
=
0
;
this
.
getCpuList
();
},
/**
* format detail data
* @param {Object} row table cell
* @param {Object} detailsDataList table detail
*/
formatterDetailData
(
row
,
detailsDataList
)
{
row
.
opDetailList
=
[];
row
.
opDetailCol
=
detailsDataList
.
col_name
;
row
.
pageTotal
=
detailsDataList
.
size
;
if
(
detailsDataList
.
object
)
{
detailsDataList
.
object
.
forEach
((
k
)
=>
{
const
data
=
{};
detailsDataList
.
col_name
.
forEach
((
item
,
index
)
=>
{
if
(
item
===
'
op_info
'
)
{
data
[
item
]
=
JSON
.
stringify
(
k
[
index
]);
}
else
{
data
[
item
]
=
k
[
index
];
}
});
row
.
opDetailList
.
push
(
data
);
});
}
},
/**
* expand core type table
* @param {Object} row table cell
*/
expandTypeItem
(
row
)
{
row
.
isExpanded
=
!
row
.
isExpanded
;
if
(
row
.
isExpanded
)
{
row
.
opDetailList
=
[];
row
.
opDetailCol
=
[];
row
.
opDetailPage
.
offset
=
0
;
row
.
pageTotal
=
0
;
row
.
op_sort_condition
=
{
name
:
'
execution_time
'
,
type
:
'
descending
'
};
this
.
getCoreDetailList
(
row
);
}
},
/**
* tab change
*/
tabChange
()
{
if
(
this
.
apiType
===
'
cpu
'
&&
this
.
cpuCharts
.
device_id
!==
this
.
currentCard
)
{
this
.
initOver
=
false
;
this
.
clearCpuData
();
this
.
getCpuList
();
}
else
if
(
this
.
apiType
===
'
core
'
&&
this
.
coreCharts
.
device_id
!==
this
.
currentCard
)
{
this
.
initOver
=
false
;
this
.
clearCoreData
();
this
.
getCoreTypeList
();
}
this
.
$nextTick
(()
=>
{
this
.
resizeCallback
();
});
},
/**
* core table type change
*/
coreTableChange
()
{
if
(
this
.
statisticType
&&
!
this
.
opAllTypeList
.
opDetailCol
.
length
)
{
this
.
opAllTypeList
.
op_sort_condition
=
{
name
:
'
execution_time
'
,
type
:
'
descending
'
,
};
this
.
getCoreDetailList
(
this
.
opAllTypeList
);
}
},
/**
* operator cpu chart change
*/
cpuChartChange
()
{
this
.
setOption
(
this
.
cpuCharts
);
},
/**
* operator core chart change
*/
coreChartChange
()
{
this
.
setOption
(
this
.
coreCharts
);
},
/**
* set chart option
* @param {Object} chart chart
*/
setOption
(
chart
)
{
const
option
=
{};
if
(
!
chart
.
type
)
{
option
.
legend
=
{
data
:
[],
orient
:
'
vertical
'
,
icon
:
'
circle
'
,
formatter
:
(
params
)
=>
{
let
legendStr
=
''
;
for
(
let
i
=
0
;
i
<
chart
.
data
.
length
;
i
++
)
{
if
(
chart
.
data
[
i
].
name
===
params
)
{
const
name
=
chart
.
data
[
i
].
name
.
length
>
10
?
`
${
chart
.
data
[
i
].
name
.
slice
(
0
,
10
)}
...`
:
chart
.
data
[
i
].
name
;
legendStr
=
`{a|
${
i
+
1
}
}{b|
${
name
}
${
chart
.
data
[
i
].
value
.
toFixed
(
3
)}
}\n{c|
${
chart
.
data
[
i
].
percent
.
toFixed
(
2
)}
%}`
;
}
}
return
legendStr
;
},
itemWidth
:
18
,
itemHeight
:
18
,
padding
:
[
0
,
50
,
0
,
0
],
top
:
'
5%
'
,
left
:
'
37%
'
,
textStyle
:
{
padding
:
[
15
,
0
,
0
,
0
],
rich
:
{
a
:
{
width
:
24
,
align
:
'
center
'
,
padding
:
[
0
,
10
,
3
,
-
26
],
color
:
'
#FFF
'
,
},
b
:
{
padding
:
[
0
,
0
,
3
,
0
],
},
c
:
{
width
:
'
100%
'
,
padding
:
[
0
,
0
,
5
,
10
],
color
:
'
#9EA4B3
'
,
fontSize
:
12
,
},
},
},
};
option
.
tooltip
=
{
trigger
:
'
item
'
,
formatter
:
(
params
)
=>
{
return
`
${
params
.
marker
}
${
params
.
data
.
name
}
${
params
.
percent
}
%`
;
},
};
option
.
series
=
[
{
type
:
'
pie
'
,
center
:
[
'
20%
'
,
'
60%
'
],
data
:
chart
.
data
,
radius
:
'
60%
'
,
lable
:
{
position
:
'
outer
'
,
alignTo
:
'
none
'
,
bleedMargin
:
5
,
},
itemStyle
:
{
normal
:
{
color
:
function
(
params
)
{
return
CommonProperty
.
pieColorArr
[
params
.
dataIndex
];
},
},
},
},
];
chart
.
data
.
forEach
((
item
)
=>
{
option
.
legend
.
data
.
push
(
item
.
name
);
});
}
else
if
(
chart
.
type
)
{
option
.
color
=
[
'
#6C92FA
'
];
option
.
tooltip
=
{
trigger
:
'
axis
'
,
};
option
.
series
=
[
{
type
:
'
bar
'
,
barWidth
:
30
,
data
:
[],
},
];
option
.
xAxis
=
{
type
:
'
category
'
,
axisLabel
:
{
interval
:
0
,
rotate
:
-
30
,
},
data
:
[],
};
option
.
grid
=
{
left
:
50
,
top
:
20
,
right
:
0
,
bottom
:
50
,
};
option
.
yAxis
=
{
type
:
'
value
'
,
};
chart
.
data
.
forEach
((
item
)
=>
{
const
name
=
this
.
apiType
===
'
cpu
'
?
item
.
op_name
:
item
.
name
;
option
.
xAxis
.
data
.
push
(
name
);
option
.
series
[
0
].
data
.
push
(
item
.
value
);
});
if
(
this
.
apiType
===
'
cpu
'
)
{
option
.
xAxis
.
axisLabel
.
formatter
=
(
params
,
dataIndex
)
=>
{
const
xAxisValue
=
chart
.
data
[
dataIndex
].
op_name
;
return
xAxisValue
.
replace
(
/^.+
\/
/g
,
''
);
};
}
}
this
.
$nextTick
(()
=>
{
const
cpuDom
=
document
.
getElementById
(
chart
.
id
);
if
(
cpuDom
)
{
chart
.
chartDom
=
echarts
.
init
(
cpuDom
,
null
);
}
else
{
if
(
chart
.
chartDom
)
{
chart
.
chartDom
.
clear
();
}
return
;
}
chart
.
chartDom
.
setOption
(
option
,
true
);
chart
.
chartDom
.
resize
();
},
10
);
},
/**
* show operator info deteail
* @param {Object} cellData cell data
* @param {Object} column column
*/
showInfoDetail
(
cellData
,
column
)
{
if
(
column
.
property
!==
'
op_info
'
||
!
cellData
||
!
cellData
.
op_info
)
{
return
;
}
this
.
showDialogData
(
cellData
.
op_info
,
column
);
},
/**
* The detailed information is displayed in the dialog box.
* @param {String} val
* @param {Object} column
*/
showDialogData
(
val
,
column
)
{
this
.
detailsDataList
=
[];
if
(
typeof
val
!==
'
string
'
||
val
==
'
{}
'
)
{
return
;
}
else
{
const
isJson
=
this
.
isJSON
(
val
);
if
(
!
isJson
)
{
return
;
}
}
this
.
$nextTick
(()
=>
{
this
.
rowName
=
`
${
column
.
label
}${
this
.
$t
(
'
dataTraceback.details
'
)}
`
;
this
.
detailsDialogVisible
=
true
;
this
.
detailsDataList
=
this
.
formateJsonString
(
val
);
});
},
/**
* Checks whether the value is a JSON character string.
* @param {String} val
* @return {Boolean}
*/
isJSON
(
val
)
{
try
{
JSON
.
parse
(
val
);
return
true
;
}
catch
(
e
)
{
return
false
;
}
},
/**
* Converts JSON strings.
* @param {String} str
* @return {Array}
*/
formateJsonString
(
str
)
{
if
(
!
str
)
{
return
[];
}
const
resultArr
=
[];
const
dataObj
=
JSON
.
parse
(
str
);
const
keys
=
Object
.
keys
(
dataObj
);
keys
.
forEach
((
key
,
index
)
=>
{
const
tempData
=
{
id
:
index
+
1
,
hasChildren
:
false
,
key
:
key
,
value
:
''
,
};
if
(
typeof
dataObj
[
key
]
===
this
.
objectType
&&
dataObj
[
key
]
!==
null
)
{
if
(
!
(
dataObj
[
key
]
instanceof
Array
))
{
tempData
.
hasChildren
=
true
;
tempData
.
children
=
[];
Object
.
keys
(
dataObj
[
key
]).
forEach
((
k
,
j
)
=>
{
const
item
=
{};
item
.
key
=
k
;
item
.
value
=
dataObj
[
key
][
k
];
item
.
id
=
`
${
new
Date
().
getTime
()}
`
+
`
${
this
.
$store
.
state
.
tableId
}
`
;
this
.
$store
.
commit
(
'
increaseTableId
'
);
tempData
.
children
.
push
(
item
);
});
}
tempData
.
value
=
JSON
.
stringify
(
dataObj
[
key
]);
}
else
{
tempData
.
value
=
dataObj
[
key
];
}
resultArr
.
push
(
tempData
);
});
return
resultArr
;
},
loadDataListChildren
(
tree
,
treeNode
,
resolve
)
{
setTimeout
(()
=>
{
resolve
(
tree
.
children
);
});
},
/**
* window resize
*/
resizeCallback
()
{
if
(
this
.
coreCharts
.
chartDom
&&
this
.
apiType
===
'
core
'
)
{
this
.
coreCharts
.
chartDom
.
resize
();
}
if
(
this
.
cpuCharts
.
chartDom
&&
this
.
apiType
===
'
cpu
'
)
{
this
.
cpuCharts
.
chartDom
.
resize
();
}
},
},
mounted
()
{
if
(
this
.
train_id
)
{
document
.
title
=
`
${
decodeURIComponent
(
this
.
train_id
)}
-
${
this
.
$t
(
'
profiling.operatorDetail
'
)}
-MindInsight`
;
}
else
{
document
.
title
=
`
${
this
.
$t
(
'
profiling.operatorDetail
'
)}
-MindInsight`
;
}
window
.
addEventListener
(
'
resize
'
,
this
.
resizeCallback
,
false
);
},
};
</
script
>
<
style
lang=
"scss"
>
.operator
{
height
:
100%
;
}
.clear
{
clear
:
both
;
}
.el-tabs__item
{
color
:
#6c7280
;
font-size
:
16px
;
line-height
:
36px
;
height
:
36px
;
}
.el-tabs__item.is-active
{
color
:
#00a5a7
;
font-weight
:
bold
;
}
.operator-title
{
padding
:
0
15px
;
font-size
:
16px
;
font-weight
:
bold
;
}
.cl-profiler
{
height
:
calc
(
100%
-
21px
);
overflow-y
:
auto
;
width
:
100%
;
background
:
#fff
;
padding
:
0
16px
;
overflow
:
hidden
;
.el-tabs
{
height
:
100%
;
.el-tabs__header
{
margin-bottom
:
10px
;
}
}
.el-tabs__content
{
height
:
calc
(
100%
-
46px
);
}
.el-tab-pane
{
height
:
100%
;
}
.cl-search-box
{
float
:
right
;
margin-bottom
:
10px
;
}
.cl-profiler-top
{
height
:
36%
;
}
.cl-profiler-bottom
{
height
:
64%
;
padding-top
:
10px
;
}
.cpu-tab
{
.cl-profiler-top
{
height
:
calc
(
36%
+
32px
);
}
.cl-profiler-bottom
{
height
:
calc
(
64%
-
32px
);
}
}
.profiler-title
{
font-size
:
16px
;
font-weight
:
bold
;
line-height
:
32px
;
display
:
inline-block
;
}
.cl-profiler-echarts
{
width
:
100%
;
height
:
calc
(
100%
-
32px
);
display
:
inline-block
;
position
:
relative
;
overflow
:
auto
;
#cpu-echarts
,
#core-echarts
{
width
:
100%
;
height
:
100%
;
min-width
:
1300px
;
min-height
:
232px
;
}
}
.chart-radio-group
{
float
:
right
;
}
.el-radio-group
{
.el-radio-button--small
.el-radio-button__inner
{
height
:
30px
;
width
:
70px
;
font-size
:
14px
;
line-height
:
10px
;
}
}
.cl-profiler-bar
{
display
:
inline-block
;
width
:
calc
(
100%
-
400px
);
vertical-align
:
top
;
height
:
100%
;
padding
:
20px
;
}
.cl-profiler-table-type
{
display
:
inline-block
;
width
:
calc
(
100%
-
400px
);
vertical-align
:
top
;
height
:
100%
;
}
.el-pagination
{
margin
:
7px
0
;
float
:
right
;
}
.details-data-list
{
.el-table
{
th
{
padding
:
10px
0
;
border-top
:
1px
solid
#ebeef5
;
.cell
{
border-left
:
1px
solid
#d9d8dd
;
height
:
14px
;
line-height
:
14px
;
}
}
th
:first-child
{
.cell
{
border-left
:
none
;
}
}
th
:nth-child
(
2
),
td
:nth-child
(
2
)
{
max-width
:
30%
;
}
td
{
padding
:
8px
0
;
}
}
.el-table__row--level-0
td
:first-child:after
{
width
:
20px
;
height
:
1px
;
background
:
#ebeef5
;
z-index
:
11
;
position
:
absolute
;
left
:
0
;
bottom
:
-1px
;
content
:
''
;
display
:
block
;
}
.el-table__row--level-1
{
td
{
padding
:
4px
0
;
position
:
relative
;
}
td
:first-child::before
{
width
:
42px
;
background
:
#f0fdfd
;
border-right
:
2px
#00a5a7
solid
;
z-index
:
10
;
position
:
absolute
;
left
:
0
;
top
:
-1px
;
bottom
:
0px
;
content
:
''
;
display
:
block
;
}
}
.el-table__row--level-1
:first-child
{
td
:first-child::before
{
bottom
:
0
;
}
}
.el-dialog__title
{
font-weight
:
bold
;
}
.el-dialog__body
{
max-height
:
500px
;
padding-top
:
10px
;
overflow
:
auto
;
.details-data-title
{
margin-bottom
:
20px
;
}
}
}
.el-table__expanded-cell
[
class
*=
'cell'
]
{
padding
:
0
;
}
.expand-table
{
position
:
relative
;
padding-left
:
44px
;
}
.
expand-table
:
:
before
{
content
:
''
;
position
:
absolute
;
left
:
0
;
top
:
0
;
height
:
100%
;
background
:
#f0fdfd
;
width
:
42px
;
border-right
:
2px
#00a5a7
solid
;
}
.el-radio-button
:last-child
.el-radio-button__inner
,
.el-radio-button
:first-child
.el-radio-button__inner
{
border-radius
:
0
;
}
.image-noData
{
width
:
100%
;
height
:
450px
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
flex-direction
:
column
;
p
{
font-size
:
16px
;
padding-top
:
10px
;
}
}
}
</
style
>
mindinsight/ui/src/views/train-manage/profiling-dashboard.vue
0 → 100644
浏览文件 @
4975cb72
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<
template
>
<div
class=
"pro-router-wrap"
>
<div
class=
"pro-router-left"
>
<div
class=
"step-trace"
>
<div
class=
"title-wrap"
>
<div
class=
"title"
>
{{
$t
(
'
profiling.stepTrace
'
)
}}
</div>
<div
class=
"view-detail"
>
<button
@
click=
"viewDetail('step-trace')"
:disabled=
"svg.noData && svg.data.length === 0"
:class=
"
{disabled:svg.noData
&&
svg.data.length === 0}">
{{
$t
(
'
profiling.viewDetail
'
)
}}
<i
class=
"el-icon-d-arrow-right"
></i></button>
</div>
<div
class=
"tip-icon"
v-show=
"false"
>
<el-tooltip
content=
""
placement=
"top"
effect=
"light"
>
<i
class=
"el-icon-info"
></i>
</el-tooltip>
</div>
</div>
<div
class=
"trace-container"
>
<div
id=
"trace"
class=
"training-trace"
>
<svg
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
height=
"100%"
width=
"100%"
>
<defs>
<marker
id=
"marker_end"
refX=
"5"
refY=
"4"
markerWidth=
"10"
markerHeight=
"8"
orient=
"auto"
>
<path
d=
"M1,1 L1,7 L9,4 z"
fill=
"#E6EBF5"
stroke=
"#E6EBF5"
></path>
</marker>
<marker
id=
"marker_start"
refX=
"5"
refY=
"4"
markerWidth=
"10"
markerHeight=
"8"
orient=
"auto"
>
<path
d=
"M9,1 L9,7 L1,4 z"
fill=
"#E6EBF5"
stroke=
"#E6EBF5"
></path>
</marker>
</defs>
</svg>
</div>
<div
class=
"image-noData"
v-if=
"svg.noData"
>
<div>
<img
:src=
"require('@/assets/images/nodata.png')"
alt=
""
/>
</div>
<p>
{{
$t
(
"
public.noData
"
)
}}
</p>
</div>
</div>
</div>
<div
class=
"minddata"
>
<div
class=
"title-wrap"
>
<div
class=
"title"
>
{{
$t
(
'
profiling.mindData
'
)
}}
</div>
<div
class=
"view-detail"
v-if=
"false"
>
<button
@
click=
"viewDetail('minddata')"
>
{{
$t
(
'
profiling.viewDetail
'
)
}}
<i
class=
"el-icon-d-arrow-right"
></i></button>
</div>
</div>
<div
class=
"coming-soon-content"
>
<div
class=
"coming-soon-container"
>
<img
:src=
"require('@/assets/images/coming-soon.png')"
/>
<p
class=
'coming-soon-text'
>
{{
$t
(
"
public.stayTuned
"
)
}}
</p>
</div>
</div>
</div>
</div>
<div
class=
"pro-router-right"
>
<div
class=
"op-time-consume"
>
<div
class=
"title-wrap"
>
<div
class=
"title"
>
{{
$t
(
'
profiling.rankOfOperator
'
)
}}
</div>
<div
class=
"view-detail"
>
<button
@
click=
"viewDetail('operator')"
:disabled=
"pieChart.noData && pieChart.data.length === 0"
:class=
"
{disabled:pieChart.noData
&&
pieChart.data.length === 0}">
{{
$t
(
'
profiling.viewDetail
'
)
}}
<i
class=
"el-icon-d-arrow-right"
></i></button>
</div>
</div>
<div
class=
"image-noData"
v-if=
"pieChart.noData && pieChart.data.length === 0"
>
<div>
<img
:src=
"require('@/assets/images/nodata.png')"
alt=
""
/>
</div>
<p>
{{
$t
(
"
public.noData
"
)
}}
</p>
</div>
<div
class=
"op-time-content"
>
<div
id=
"pieChart"
class=
"pie-chart"
v-if=
"pieChart.data.length"
></div>
<div
class=
"time-list"
v-if=
"pieChart.data.length"
>
<ul>
<li
v-for=
"(item, index) in pieChart.topN"
:key=
"index"
class=
"item"
>
<span
class=
"index"
:style=
"
{'background-color': pieChart.colorList[index]}">
{{
index
+
1
}}
</span>
<span
class=
"name"
>
{{
item
.
name
}}
</span>
<span
class=
"num"
>
{{
item
.
frequency
+
$t
(
'
profiling.times
'
)
}}
</span>
<span
class=
"time"
>
<span
class=
"bar"
:style=
"
{width: item.time / pieChart.topN[0].time * 100 + '%'}">
</span>
<span
class=
"value"
>
{{
item
.
time
}}
ms
</span>
</span>
</li>
</ul>
</div>
</div>
</div>
<div
class=
"time-line"
>
<div
class=
"title-wrap"
>
<div
class=
"title"
>
{{
$t
(
'
profiling.timeLine
'
)
}}
</div>
<div
class=
"view-detail"
v-show=
"false"
>
<a
@
click=
"toPerfetto()"
>
{{
$t
(
'
profiling.viewDetail
'
)
}}
<i
class=
"el-icon-d-arrow-right"
></i></a>
</div>
</div>
<div
class=
"coming-soon-content"
>
<div
class=
"coming-soon-container"
>
<img
:src=
"require('@/assets/images/coming-soon.png')"
/>
<p
class=
'coming-soon-text'
>
{{
$t
(
"
public.stayTuned
"
)
}}
</p>
</div>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
import
echarts
from
'
echarts
'
;
import
RequestService
from
'
../../services/request-service
'
;
import
CommonProperty
from
'
../../common/common-property
'
;
export
default
{
data
()
{
return
{
svg
:
{
data
:
[],
svgPadding
:
20
,
totalWidth
:
0
,
totalTime
:
0
,
rowHeight
:
60
,
markerPadding
:
4
,
namespaceURI
:
'
http://www.w3.org/2000/svg
'
,
resizeTimer
:
null
,
colorList
:
[
[
'
#A6DD82
'
,
'
#edf8e6
'
],
[
'
#6CBFFF
'
,
'
#e2f2ff
'
],
[
'
#fa8e5b
'
,
'
#fff4de
'
],
[
'
#01a5a7
'
,
'
#cceded
'
],
],
colorIndex
:
0
,
noData
:
false
,
},
trainingJobId
:
this
.
$route
.
query
.
id
,
summaryPath
:
this
.
$route
.
query
.
dir
,
relativePath
:
this
.
$route
.
query
.
path
,
currentCard
:
''
,
pieChart
:
{
chartDom
:
null
,
data
:
[],
noData
:
false
,
topN
:
[],
colorList
:
[
'
#6C92FA
'
,
'
#6CBFFF
'
,
'
#4EDED2
'
,
'
#7ADFA0
'
,
'
#A6DD82
'
],
},
};
},
mounted
()
{},
watch
:
{
'
$parent.curDashboardInfo
'
:
{
handler
(
newValue
,
oldValue
)
{
if
(
newValue
.
query
.
dir
&&
newValue
.
query
.
id
&&
newValue
.
query
.
path
)
{
this
.
summaryPath
=
newValue
.
query
.
dir
;
this
.
trainingJobId
=
newValue
.
query
.
id
;
this
.
relativePath
=
newValue
.
query
.
path
;
this
.
currentCard
=
newValue
.
curCardNum
;
if
(
this
.
trainingJobId
)
{
document
.
title
=
`
${
decodeURIComponent
(
this
.
trainingJobId
,
)}
-
${
this
.
$t
(
'
profiling.profilingDashboard
'
)}
-MindInsight`
;
}
else
{
document
.
title
=
`
${
this
.
$t
(
'
profiling.profilingDashboard
'
,
)}
-MindInsight`
;
}
this
.
init
();
}
},
deep
:
true
,
immediate
:
true
,
},
},
methods
:
{
init
()
{
this
.
queryTrainingTrace
();
this
.
initPieChart
();
window
.
addEventListener
(
'
resize
'
,
this
.
resizeTrace
,
false
);
this
.
$bus
.
$on
(
'
resize
'
,
this
.
resizeTrace
);
},
viewDetail
(
path
)
{
this
.
$router
.
push
({
path
,
query
:
{
id
:
this
.
trainingJobId
,
dir
:
this
.
summaryPath
,
path
:
this
.
relativePath
,
},
});
},
setPieOption
()
{
const
option
=
{};
option
.
tooltip
=
{
trigger
:
'
item
'
,
formatter
:
(
params
)
=>
{
return
`
${
params
.
marker
}
${
params
.
data
.
name
}
${
params
.
percent
}
%`
;
},
};
option
.
series
=
[
{
type
:
'
pie
'
,
center
:
[
'
50%
'
,
'
50%
'
],
data
:
this
.
pieChart
.
data
,
radius
:
'
50%
'
,
lable
:
{
position
:
'
outer
'
,
alignTo
:
'
none
'
,
bleedMargin
:
5
,
},
itemStyle
:
{
normal
:
{
color
:
function
(
params
)
{
return
CommonProperty
.
pieColorArr
[
params
.
dataIndex
];
},
},
},
},
];
this
.
$nextTick
(()
=>
{
const
dom
=
document
.
getElementById
(
'
pieChart
'
);
if
(
dom
)
{
this
.
pieChart
.
chartDom
=
echarts
.
init
(
dom
,
null
);
}
else
{
if
(
this
.
pieChart
.
chartDom
)
{
this
.
pieChart
.
chartDom
.
clear
();
}
return
;
}
this
.
pieChart
.
chartDom
.
setOption
(
option
,
true
);
this
.
pieChart
.
chartDom
.
resize
();
},
10
);
},
initPieChart
()
{
const
params
=
{};
params
.
params
=
{
profile
:
this
.
summaryPath
,
train_id
:
this
.
trainingJobId
,
};
params
.
body
=
{
op_type
:
'
aicore_type
'
,
device_id
:
this
.
currentCard
,
filter_condition
:
{},
sort_condition
:
{
name
:
'
execution_time
'
,
type
:
'
descending
'
,
},
};
RequestService
.
getProfilerOpData
(
params
)
.
then
((
res
)
=>
{
if
(
res
&&
res
.
data
)
{
if
(
res
.
data
.
object
)
{
this
.
pieChart
.
data
=
[];
res
.
data
.
object
.
forEach
((
item
)
=>
{
if
(
this
.
pieChart
.
data
&&
this
.
pieChart
.
data
.
length
<
19
)
{
this
.
pieChart
.
data
.
push
({
name
:
item
[
0
],
value
:
item
[
1
],
frequency
:
item
[
2
],
percent
:
item
[
3
],
});
}
else
{
if
(
!
this
.
pieChart
.
data
[
19
])
{
this
.
pieChart
.
data
[
19
]
=
{
name
:
'
Other
'
,
value
:
0
,
percent
:
0
,
};
}
this
.
pieChart
.
data
[
19
].
value
+=
item
[
1
];
this
.
pieChart
.
data
[
19
].
percent
+=
item
[
3
];
}
});
this
.
setPieOption
();
if
(
this
.
pieChart
.
data
.
length
===
0
)
{
this
.
pieChart
.
noData
=
true
;
}
this
.
pieChart
.
topN
=
this
.
pieChart
.
data
.
slice
(
0
,
Math
.
min
(
this
.
pieChart
.
data
.
length
,
5
))
.
map
((
i
)
=>
{
return
{
name
:
i
.
name
,
time
:
i
.
value
,
frequency
:
i
.
frequency
,
};
});
}
}
})
.
catch
(()
=>
{
this
.
pieChart
.
noData
=
true
;
});
},
queryTrainingTrace
()
{
const
params
=
{
dir
:
this
.
relativePath
,
type
:
0
,
device_id
:
this
.
currentCard
,
};
RequestService
.
queryTrainingTrace
(
params
).
then
(
(
res
)
=>
{
if
(
res
.
data
&&
res
.
data
.
training_trace_graph
&&
res
.
data
.
training_trace_graph
.
length
)
{
this
.
svg
.
noData
=
false
;
document
.
querySelector
(
'
#trace
'
).
style
.
height
=
`
${
res
.
data
.
training_trace_graph
.
length
*
this
.
svg
.
rowHeight
}
px`
;
this
.
svg
.
data
=
JSON
.
parse
(
JSON
.
stringify
(
res
.
data
.
training_trace_graph
),
);
this
.
removeTrace
();
setTimeout
(()
=>
{
this
.
dealTraceData
();
},
100
);
}
else
{
document
.
querySelector
(
'
#trace
'
).
style
.
height
=
'
0px
'
;
this
.
svg
.
noData
=
true
;
this
.
svg
.
data
=
[];
this
.
removeTrace
();
}
},
(
error
)
=>
{
document
.
querySelector
(
'
#trace
'
).
style
.
height
=
'
0px
'
;
this
.
svg
.
noData
=
true
;
this
.
svg
.
data
=
[];
this
.
removeTrace
();
},
);
},
dealTraceData
()
{
const
traceDom
=
document
.
querySelector
(
'
#trace
'
);
if
(
traceDom
)
{
this
.
svg
.
totalWidth
=
traceDom
.
offsetWidth
-
this
.
svg
.
svgPadding
*
2
;
if
(
this
.
svg
.
data
[
0
]
&&
this
.
svg
.
data
[
0
].
length
)
{
const
svg
=
document
.
querySelector
(
'
#trace svg
'
);
this
.
svg
.
totalTime
=
this
.
svg
.
data
[
0
][
0
].
duration
;
if
(
this
.
svg
.
totalTime
)
{
this
.
svg
.
data
.
forEach
((
row
,
index
)
=>
{
if
(
row
&&
row
.
length
)
{
const
dashedLine
=
this
.
addDashedLine
(
index
);
svg
.
insertBefore
(
dashedLine
,
svg
.
querySelector
(
'
g
'
));
row
.
forEach
((
i
)
=>
{
if
(
i
.
duration
)
{
const
tempDom
=
i
.
name
?
this
.
createRect
(
i
,
index
)
:
this
.
createArrow
(
i
,
index
);
svg
.
insertBefore
(
tempDom
,
svg
.
querySelector
(
'
g
'
));
}
});
}
});
}
}
else
{
this
.
removeTrace
();
}
}
},
addDashedLine
(
index
)
{
const
x1
=
this
.
svg
.
svgPadding
;
const
x2
=
this
.
svg
.
svgPadding
+
this
.
svg
.
totalWidth
;
const
y
=
index
*
this
.
svg
.
rowHeight
;
const
line
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
line
.
setAttribute
(
'
x1
'
,
x1
);
line
.
setAttribute
(
'
y1
'
,
y
);
line
.
setAttribute
(
'
x2
'
,
x2
);
line
.
setAttribute
(
'
y2
'
,
y
);
line
.
setAttribute
(
'
style
'
,
'
stroke:#E2E2E2;stroke-width:1
'
);
line
.
setAttribute
(
'
stroke-dasharray
'
,
'
5 5
'
);
const
g
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
g
.
appendChild
(
line
);
return
g
;
},
createRect
(
data
,
rowIndex
)
{
const
color
=
this
.
svg
.
colorList
[
this
.
svg
.
colorIndex
++
%
4
];
const
height
=
40
;
const
width
=
(
data
.
duration
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
;
const
x1
=
(
data
.
start
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
+
this
.
svg
.
svgPadding
;
const
y1
=
rowIndex
*
this
.
svg
.
rowHeight
+
(
this
.
svg
.
rowHeight
-
height
)
/
2
;
const
g
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
const
gChild
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
const
rect
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
rect
'
);
rect
.
setAttribute
(
'
x
'
,
x1
);
rect
.
setAttribute
(
'
y
'
,
y1
);
rect
.
setAttribute
(
'
height
'
,
height
);
rect
.
setAttribute
(
'
width
'
,
width
);
rect
.
setAttribute
(
'
style
'
,
`fill:
${
color
[
1
]}
;stroke:
${
color
[
1
]}
;`
);
const
foreignObject
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
foreignObject
'
,
);
foreignObject
.
setAttribute
(
'
x
'
,
x1
);
foreignObject
.
setAttribute
(
'
y
'
,
y1
);
foreignObject
.
setAttribute
(
'
height
'
,
height
);
foreignObject
.
setAttribute
(
'
width
'
,
width
);
foreignObject
.
setAttribute
(
'
style
'
,
`overflow:hidden;text-align:center;text-overflow:ellipsis;`
+
`white-space:nowrap;font-size:12px;line-height:
${
height
}
px;color:
${
color
[
0
]}
`
,
);
foreignObject
.
textContent
=
`
${
data
.
name
}
:
${
data
.
duration
.
toFixed
(
4
)}
ms`
;
const
title
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
title
'
);
title
.
textContent
=
`
${
data
.
name
}
:
${
data
.
duration
.
toFixed
(
4
)}
ms`
;
gChild
.
appendChild
(
rect
);
gChild
.
appendChild
(
foreignObject
);
gChild
.
appendChild
(
title
);
g
.
appendChild
(
gChild
);
return
g
;
},
createArrow
(
data
,
rowIndex
)
{
const
width
=
(
data
.
duration
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
;
const
x1
=
(
data
.
start
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
+
this
.
svg
.
markerPadding
+
this
.
svg
.
svgPadding
;
const
x2
=
x1
+
width
-
this
.
svg
.
markerPadding
*
2
;
const
y
=
rowIndex
*
this
.
svg
.
rowHeight
+
this
.
svg
.
rowHeight
/
2
;
const
g
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
const
line
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
line
.
setAttribute
(
'
x1
'
,
x1
);
line
.
setAttribute
(
'
y1
'
,
y
);
line
.
setAttribute
(
'
x2
'
,
x2
);
line
.
setAttribute
(
'
y2
'
,
y
);
line
.
setAttribute
(
'
style
'
,
'
stroke:#E6EBF5;stroke-width:1
'
);
line
.
setAttribute
(
'
marker-end
'
,
'
url(#marker_end)
'
);
line
.
setAttribute
(
'
marker-start
'
,
'
url(#marker_start)
'
);
const
text
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
text
'
);
text
.
textContent
=
`
${
data
.
duration
.
toFixed
(
4
)}
ms`
;
const
textWidth
=
this
.
getTextWidth
(
text
.
textContent
);
text
.
setAttribute
(
'
x
'
,
Math
.
max
(
0
,
(
x2
-
x1
)
/
2
+
x1
-
textWidth
/
2
));
text
.
setAttribute
(
'
y
'
,
y
-
6
);
text
.
setAttribute
(
'
font-size
'
,
12
);
text
.
setAttribute
(
'
fill
'
,
'
#6c7280
'
);
const
startLine
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
startLine
.
setAttribute
(
'
x1
'
,
x1
-
this
.
svg
.
markerPadding
);
startLine
.
setAttribute
(
'
y1
'
,
y
-
this
.
svg
.
rowHeight
/
4
);
startLine
.
setAttribute
(
'
x2
'
,
x1
-
this
.
svg
.
markerPadding
);
startLine
.
setAttribute
(
'
y2
'
,
y
+
this
.
svg
.
rowHeight
/
4
);
startLine
.
setAttribute
(
'
style
'
,
'
stroke:#E6EBF5;stroke-width:1
'
);
g
.
appendChild
(
startLine
);
const
endLine
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
endLine
.
setAttribute
(
'
x1
'
,
x1
+
width
-
this
.
svg
.
markerPadding
);
endLine
.
setAttribute
(
'
y1
'
,
y
-
this
.
svg
.
rowHeight
/
4
);
endLine
.
setAttribute
(
'
x2
'
,
x1
+
width
-
this
.
svg
.
markerPadding
);
endLine
.
setAttribute
(
'
y2
'
,
y
+
this
.
svg
.
rowHeight
/
4
);
endLine
.
setAttribute
(
'
style
'
,
'
stroke:#E6EBF5;stroke-width:1
'
);
g
.
appendChild
(
endLine
);
g
.
appendChild
(
line
);
g
.
appendChild
(
text
);
return
g
;
},
getTextWidth
(
text
)
{
const
body
=
document
.
querySelector
(
'
body
'
);
const
temp
=
document
.
createElement
(
'
span
'
);
temp
.
style
[
'
font-size
'
]
=
'
12px
'
;
temp
.
textContent
=
text
;
body
.
appendChild
(
temp
);
const
textWidth
=
temp
.
offsetWidth
;
body
.
removeChild
(
temp
);
return
textWidth
;
},
removeTrace
()
{
const
svgDom
=
document
.
querySelector
(
'
#trace svg
'
);
if
(
svgDom
)
{
const
gDoms
=
svgDom
.
children
;
if
(
gDoms
)
{
for
(
let
i
=
0
;
i
<
gDoms
.
length
;
i
++
)
{
if
(
gDoms
[
i
].
nodeName
===
'
g
'
)
{
svgDom
.
removeChild
(
gDoms
[
i
--
]);
}
}
}
}
},
resizeTrace
()
{
if
(
this
.
svg
.
resizeTimer
)
{
clearTimeout
(
this
.
svg
.
resizeTimer
);
}
this
.
svg
.
resizeTimer
=
setTimeout
(()
=>
{
this
.
removeTrace
();
this
.
dealTraceData
();
this
.
svg
.
resizeTimer
=
null
;
},
500
);
},
stringToUint8Array
(
str
)
{
const
arr
=
[];
for
(
let
i
=
0
,
strLen
=
str
.
length
;
i
<
strLen
;
i
++
)
{
arr
.
push
(
str
.
charCodeAt
(
i
));
}
return
new
Uint8Array
(
arr
);
},
},
destroyed
()
{
window
.
removeEventListener
(
'
resize
'
,
this
.
resizeTrace
,
false
);
this
.
$bus
.
$off
(
'
resize
'
);
},
};
</
script
>
<
style
lang=
"scss"
>
.pro-router-wrap
{
height
:
100%
;
&
>
div
{
float
:
left
;
height
:
100%
;
&
>
div
{
border
:
1px
solid
#ddd
;
border-radius
:
4px
;
}
.title-wrap
{
padding
:
15px
;
.title
{
float
:
left
;
font-weight
:
bold
;
font-size
:
16px
;
}
.tip-icon
{
float
:
right
;
margin-right
:
18px
;
font-size
:
20px
;
.el-icon-warning
{
cursor
:
pointer
;
&
:hover::before
{
color
:
#00a5a7
;
}
}
}
.view-detail
{
float
:
right
;
cursor
:
pointer
;
color
:
#00a5a7
;
font-size
:
12px
;
height
:
24px
;
line-height
:
24px
;
a
{
color
:
#00a5a7
!
important
;
padding-right
:
6px
;
}
button
{
color
:
#00a5a7
;
border
:
none
;
background-color
:
#fff
;
cursor
:
pointer
;
}
button
.disabled
{
cursor
:
not
-
allowed
;
color
:
#c0c4cc
;
}
}
&
::after
{
content
:
''
;
clear
:
both
;
display
:
block
;
}
}
.coming-soon-content
{
height
:
calc
(
100%
-
50px
);
position
:
relative
;
.coming-soon-container
{
text-align
:
center
;
position
:
absolute
;
top
:
50%
;
left
:
50%
;
border-radius
:
5px
;
-webkit-transform
:
translate
(
-50%
,
-50%
);
-moz-transform
:
translate
(
-50%
,
-50%
);
transform
:
translate
(
-50%
,
-50%
);
}
.coming-soon-text
{
font-size
:
16px
;
}
}
}
.pro-router-left
{
width
:
calc
(
100%
-
350px
);
padding-right
:
15px
;
.step-trace
{
height
:
45%
;
margin-bottom
:
15px
;
.trace-container
{
width
:
100%
;
height
:
calc
(
100%
-
50px
);
overflow
:
auto
;
.training-trace
{
position
:
relative
;
height
:
0
;
}
}
}
.minddata
{
height
:
calc
(
55%
-
15px
);
}
}
.pro-router-right
{
width
:
350px
;
.op-time-consume
{
height
:
calc
(
60%
-
15px
);
margin-bottom
:
15px
;
.time-list
{
height
:
calc
(
40%
-
52px
);
.item
{
height
:
25px
;
line-height
:
25px
;
padding
:
0
20px
;
&
>
span
{
display
:
inline-block
;
height
:
100%
;
vertical-align
:
middle
;
}
.index
{
color
:
white
;
background-color
:
rgb
(
108
,
146
,
250
);
width
:
20px
;
height
:
20px
;
border-radius
:
20px
;
text-align
:
center
;
vertical-align
:
middle
;
line-height
:
20px
;
}
.name
{
margin-left
:
10px
;
width
:
calc
(
50%
-
30px
);
text-overflow
:
ellipsis
;
overflow
:
hidden
;
}
.num
{
width
:
20%
;
}
.time
{
width
:
30%
;
position
:
relative
;
span
{
display
:
inline-block
;
position
:
absolute
;
left
:
0
;
height
:
20px
;
}
.bar
{
background-color
:
#cceded
;
top
:
2px
;
}
.value
{
line-height
:
25px
;
height
:
25px
;
}
}
}
}
}
.time-line
{
height
:
40%
;
overflow
:
hidden
;
}
}
.op-time-content
{
height
:
calc
(
100%
-
54px
);
overflow
:
auto
;
}
.pie-chart
{
width
:
100%
;
height
:
260px
;
}
.image-noData
{
width
:
100%
;
height
:
calc
(
100%
-
52px
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
flex-direction
:
column
;
p
{
font-size
:
16px
;
padding-top
:
10px
;
}
}
}
</
style
>
mindinsight/ui/src/views/train-manage/profiling.vue
0 → 100644
浏览文件 @
4975cb72
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<
template
>
<div
class=
"prof-wrap"
>
<div
class=
"prof-content"
>
<div
class=
"prof-content-left"
:class=
"
{collapse:collapse}">
<div
class=
"helper"
v-show=
"!collapse"
>
<div
class=
"cur-card"
>
<label>
{{
$t
(
'
profiling.curCard
'
)
}}
</label>
<el-select
v-model=
"curDashboardInfo.curCardNum"
class=
"card-select"
:placeholder=
"$t('public.select')"
>
<el-option
v-for=
"item in CardNumArr"
:key=
"item.value"
:label=
"item.value + $t('operator.card')"
:value=
"item.value"
>
</el-option>
</el-select>
</div>
<div
class=
"helper-title"
>
{{
$t
(
"
profiling.smartHelper
"
)
}}
</div>
</div>
<div
class=
"collapse-btn"
:class=
"
{collapse:collapse}"
@click="collapseLeft()">
</div>
</div>
<div
class=
"prof-content-right"
:class=
"
{collapse:collapse}">
<router-view></router-view>
<div
class=
"close"
@
click=
"backToDdashboard"
v-if=
"$route.path !== '/profiling/profiling-dashboard'"
>
<img
src=
"@/assets/images/close-page.png"
>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
import
RequestService
from
'
../../services/request-service
'
;
export
default
{
data
()
{
return
{
CardNumArr
:
[],
collapse
:
false
,
curDashboardInfo
:
{
curCardNum
:
''
,
query
:
{},
},
};
},
watch
:
{},
mounted
()
{
this
.
$nextTick
(()
=>
{
this
.
init
();
});
},
methods
:
{
init
()
{
if
(
this
.
$route
.
query
&&
this
.
$route
.
query
.
id
&&
this
.
$route
.
query
.
dir
)
{
this
.
curDashboardInfo
.
query
.
id
=
this
.
$route
.
query
.
id
;
this
.
curDashboardInfo
.
query
.
dir
=
this
.
$route
.
query
.
dir
;
this
.
curDashboardInfo
.
query
.
path
=
this
.
$route
.
query
.
path
;
this
.
getDeviceList
();
}
else
{
this
.
curDashboardInfo
.
query
.
trainingJobId
=
''
;
this
.
curDashboardInfo
.
query
.
dir
=
''
;
this
.
curDashboardInfo
.
query
.
path
=
''
;
this
.
$message
.
error
(
this
.
$t
(
'
trainingDashboard.invalidId
'
));
}
},
getDeviceList
()
{
const
params
=
{
profile
:
this
.
curDashboardInfo
.
query
.
dir
,
train_id
:
this
.
curDashboardInfo
.
query
.
id
,
};
RequestService
.
getProfilerDeviceData
(
params
)
.
then
((
res
)
=>
{
if
(
res
&&
res
.
data
)
{
const
deviceList
=
res
.
data
;
if
(
deviceList
.
length
)
{
deviceList
.
forEach
((
item
)
=>
{
this
.
CardNumArr
.
push
({
value
:
item
,
});
});
this
.
curDashboardInfo
.
curCardNum
=
this
.
CardNumArr
[
0
].
value
;
}
}
else
{
this
.
CardNumArr
=
[];
this
.
curDashboardInfo
.
curCardNum
=
''
;
}
})
.
catch
(()
=>
{});
},
backToDdashboard
()
{
this
.
$router
.
push
({
path
:
'
/profiling/profiling-dashboard
'
,
query
:
{
dir
:
this
.
curDashboardInfo
.
query
.
dir
,
id
:
this
.
curDashboardInfo
.
query
.
id
,
path
:
this
.
curDashboardInfo
.
query
.
path
,
},
});
},
collapseLeft
()
{
this
.
collapse
=
!
this
.
collapse
;
this
.
$bus
.
$emit
(
'
resize
'
);
},
},
};
</
script
>
<
style
lang=
"scss"
>
.prof-wrap
{
height
:
100%
;
background
:
#fff
;
.prof-content
{
height
:
100%
;
padding
:
32px
32px
32px
0
;
&
>
div
{
float
:
left
;
height
:
100%
;
}
.prof-content-left
{
width
:
25%
;
transition
:
width
0
.2s
;
position
:
relative
;
.el-input__inner
{
padding
:
0
10px
;
}
.helper
{
padding
:
32px
;
height
:
100%
;
margin-left
:
32px
;
background
:
#edf0f5
;
.cur-card
{
margin-bottom
:
32px
;
.card-select
{
width
:
calc
(
100%
-
70px
);
}
&
>
label
{
margin-right
:
14px
;
}
}
.helper-title
{
font-size
:
20px
;
font-weight
:
bold
;
margin-bottom
:
32px
;
.el-icon-rank
{
float
:
right
;
cursor
:
pointer
;
}
}
}
.collapse-btn
{
position
:
absolute
;
right
:
-21px
;
width
:
31px
;
height
:
100px
;
top
:
50%
;
margin-top
:
-50px
;
cursor
:
pointer
;
line-height
:
86px
;
z-index
:
1
;
text-align
:
center
;
background-image
:
url('../../assets/images/collapse-left.svg')
;
}
.collapse-btn.collapse
{
background-image
:
url('../../assets/images/collapse-right.svg')
;
}
}
.prof-content-left.collapse
{
width
:
0
;
}
.prof-content-right
{
width
:
75%
;
padding-left
:
20px
;
transition
:
width
0
.2s
;
position
:
relative
;
.close
{
position
:
absolute
;
right
:
0
;
top
:
-10px
;
cursor
:
pointer
;
}
}
.prof-content-right.collapse
{
width
:
100%
;
}
}
}
</
style
>
mindinsight/ui/src/views/train-manage/step-trace.vue
0 → 100644
浏览文件 @
4975cb72
<!--
Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<
template
>
<div
class=
"step-trace"
>
<div
class=
"step-trace-title"
>
{{
$t
(
'
profiling.stepTraceDetail
'
)
}}
<div
class=
"pf-content-right"
>
<div
class=
"input-wrap"
>
<label>
{{
$t
(
'
profiling.stepSelect
'
)
}}
</label>
<el-select
v-model=
"selectedStep"
filterable
:placeholder=
"$t('profiling.selectStep')"
@
change=
"changeStep"
>
<el-option
v-for=
"item in steps"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
>
</el-option>
</el-select>
</div>
</div>
</div>
<div
class=
"pf-content-middle"
v-show=
"!tabsArr[0].noData && !tabsArr[1].noData && !tabsArr[2].noData && !svg.noData"
>
<div
id=
"trace-container"
>
<div
id=
"trace"
class=
"training-trace"
>
<div
:title=
"$t('graph.downloadPic')"
class=
"download-button"
@
click=
"downloadSVG"
>
</div>
<svg
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
height=
"100%"
width=
"100%"
>
<defs>
<marker
id=
"marker_end"
refX=
"5"
refY=
"4"
markerWidth=
"10"
markerHeight=
"8"
orient=
"auto"
>
<path
d=
"M1,1 L1,7 L9,4 z"
fill=
"#E6EBF5"
stroke=
"#E6EBF5"
></path>
</marker>
<marker
id=
"marker_start"
refX=
"5"
refY=
"4"
markerWidth=
"10"
markerHeight=
"8"
orient=
"auto"
>
<path
d=
"M9,1 L9,7 L1,4 z"
fill=
"#E6EBF5"
stroke=
"#E6EBF5"
></path>
</marker>
</defs>
</svg>
</div>
<div
class=
"image-noData svg"
v-if=
"svg.data.length === 0"
>
<div>
<img
:src=
"require('@/assets/images/nodata.png')"
alt=
""
/>
</div>
<p>
{{
$t
(
"
public.noData
"
)
}}
</p>
</div>
</div>
<div
v-for=
"(item,key) in tabsArr"
:key=
"key"
class=
"chart-wrap"
>
<div
class=
"title"
>
{{
item
.
name
}}
</div>
<div
class=
"rate-wrap"
>
<div
v-if=
"item.timeSummary.total_time !== undefined"
>
<span>
{{
item
.
timeLabel
}}
:
</span>
{{
item
.
timeSummary
.
total_time
}}
ms
</div>
<div
v-if=
"item.timeSummary[item.rate] !== undefined"
>
<span>
{{
item
.
rateLabel
}}
:
</span>
{{
item
.
timeSummary
[
item
.
rate
]
}}
</div>
<div
v-if=
"item.timeSummary.total_steps !== undefined"
>
<span>
{{
$t
(
'
profiling.stepNum
'
)
}}
:
</span>
{{
item
.
timeSummary
.
total_steps
}}
</div>
</div>
<div
class=
"chart"
:id=
"item.id"
v-show=
"!item.noData"
></div>
<div
class=
"image-noData"
v-if=
"item.noData"
>
<div>
<img
:src=
"require('@/assets/images/nodata.png')"
alt=
""
/>
</div>
<p>
{{
$t
(
"
public.noData
"
)
}}
</p>
</div>
</div>
</div>
<div
class=
"image-noData"
v-if=
"!(!tabsArr[0].noData && !tabsArr[1].noData && !tabsArr[2].noData && !svg.noData)"
>
<div>
<img
:src=
"require('@/assets/images/nodata.png')"
alt=
""
/>
</div>
<p>
{{
$t
(
"
public.noData
"
)
}}
</p>
</div>
</div>
</
template
>
<
script
>
import
echarts
from
'
echarts
'
;
import
RequestService
from
'
../../services/request-service
'
;
export
default
{
data
()
{
return
{
dir
:
this
.
$route
.
query
.
dir
,
train_id
:
this
.
$route
.
query
.
id
,
relativePath
:
this
.
$route
.
query
.
path
,
steps
:
[],
selectedStep
:
''
,
charts
:
[],
svg
:
{
data
:
[],
svgPadding
:
20
,
totalWidth
:
0
,
totalTime
:
0
,
rowHeight
:
60
,
markerPadding
:
4
,
namespaceURI
:
'
http://www.w3.org/2000/svg
'
,
resizeTimer
:
null
,
colorList
:
[
[
'
#A6DD82
'
,
'
#edf8e6
'
],
[
'
#6CBFFF
'
,
'
#e2f2ff
'
],
[
'
#fa8e5b
'
,
'
#fff4de
'
],
[
'
#01a5a7
'
,
'
#cceded
'
],
],
colorIndex
:
0
,
noData
:
false
,
},
deviceId
:
0
,
radio
:
this
.
$t
(
'
profiling.lterationGap
'
),
tabsArr
:
[
{
name
:
this
.
$t
(
'
profiling.lterationGap
'
),
id
:
'
iter-gap
'
,
timeSummary
:
{},
rate
:
'
iteration_interval
'
,
timeLabel
:
this
.
$t
(
'
profiling.iterGapTimeLabel
'
),
rateLabel
:
this
.
$t
(
'
profiling.iterGapRateLabel
'
),
noData
:
false
,
},
{
name
:
'
Fp+bp
'
,
id
:
'
fp-bp
'
,
timeSummary
:
{},
rate
:
'
fp_and_bp
'
,
timeLabel
:
this
.
$t
(
'
profiling.fpBpTimeLabel
'
),
rateLabel
:
this
.
$t
(
'
profiling.fpBpRateLabel
'
),
noData
:
false
,
},
{
name
:
this
.
$t
(
'
profiling.lterationTail
'
),
id
:
'
tailing
'
,
timeSummary
:
{},
rate
:
'
tail
'
,
timeLabel
:
this
.
$t
(
'
profiling.tailTimeLabel
'
),
rateLabel
:
this
.
$t
(
'
profiling.tailRateLabel
'
),
noData
:
false
,
},
],
};
},
watch
:
{
'
$parent.curDashboardInfo
'
:
{
handler
(
newValue
,
oldValue
)
{
if
(
newValue
.
curCardNum
||
newValue
.
curCardNum
===
0
)
{
this
.
dir
=
newValue
.
query
.
dir
;
this
.
train_id
=
newValue
.
query
.
id
;
this
.
deviceId
=
newValue
.
curCardNum
;
this
.
relativePath
=
newValue
.
query
.
path
;
if
(
this
.
train_id
)
{
document
.
title
=
`
${
decodeURIComponent
(
this
.
train_id
)}
-
${
this
.
$t
(
'
profiling.stepTrace
'
,
)}
-MindInsight`
;
}
else
{
document
.
title
=
`
${
this
.
$t
(
'
profiling.stepTrace
'
)}
-MindInsight`
;
}
this
.
svg
.
noData
=
false
;
this
.
tabsArr
.
forEach
((
val
)
=>
{
val
.
noData
=
false
;
});
this
.
init
();
}
},
deep
:
true
,
immediate
:
true
,
},
},
computed
:
{},
mounted
()
{},
methods
:
{
init
()
{
window
.
addEventListener
(
'
resize
'
,
this
.
resizeTrace
,
false
);
this
.
$bus
.
$on
(
'
resize
'
,
this
.
resizeTrace
);
window
.
addEventListener
(
'
resize
'
,
this
.
resizeEchart
,
false
);
this
.
$bus
.
$on
(
'
resize
'
,
this
.
resizeEchart
);
if
(
this
.
charts
.
length
)
{
this
.
charts
.
forEach
((
val
)
=>
{
val
.
clear
();
});
}
this
.
setStep
();
this
.
getTimeInfo
(
'
fp-bp
'
,
'
fp_and_bp
'
);
this
.
getTimeInfo
(
'
iter-gap
'
,
'
iteration_interval
'
);
this
.
getTimeInfo
(
'
tailing
'
,
'
tail
'
);
this
.
queryTrainingTrace
(
0
);
},
changeStep
(
value
)
{
if
(
value
===
this
.
$t
(
'
profiling.showAverage
'
))
{
value
=
0
;
}
this
.
queryTrainingTrace
(
value
);
},
getTimeInfo
(
id
,
type
)
{
const
params
=
{
dir
:
this
.
relativePath
,
type
,
device_id
:
this
.
deviceId
,
};
RequestService
.
targetTimeInfo
(
params
).
then
(
(
res
)
=>
{
if
(
res
.
data
&&
res
.
data
.
summary
)
{
const
summary
=
res
.
data
.
summary
;
Object
.
keys
(
summary
).
forEach
((
val
)
=>
{
summary
[
val
]
=
summary
[
val
];
});
this
.
tabsArr
.
forEach
((
val
)
=>
{
if
(
id
===
val
.
id
)
{
val
.
timeSummary
=
summary
;
}
});
}
if
(
res
.
data
&&
res
.
data
.
info
)
{
if
(
this
.
steps
.
length
<=
1
)
{
this
.
setStep
(
res
.
data
.
size
);
}
const
timeInfo
=
[];
Object
.
keys
(
res
.
data
.
info
).
forEach
((
val
)
=>
{
timeInfo
.
push
({
data
:
res
.
data
.
info
[
val
],
name
:
val
,
type
:
'
line
'
,
});
});
if
(
timeInfo
.
length
)
{
const
option
=
{
xAxis
:
{
type
:
'
category
'
,
data
:
this
.
steps
.
map
((
val
,
index
)
=>
index
+
1
),
name
:
'
step
'
,
},
yAxis
:
{
type
:
'
value
'
,
name
:
''
,
nameTextStyle
:
{
padding
:
[
0
,
0
,
0
,
30
],
},
},
grid
:
{
left
:
50
,
top
:
50
,
right
:
50
,
bottom
:
20
,
},
series
:
timeInfo
,
tooltip
:
{
trigger
:
'
axis
'
,
},
};
if
(
type
===
'
iteration_interval
'
)
{
option
.
yAxis
.
name
=
`
${
this
.
$t
(
'
profiling.iterationGapTime
'
,
)}
(ms)`
;
this
.
tabsArr
[
0
].
noData
=
this
.
steps
.
length
?
false
:
true
;
}
else
if
(
type
===
'
fp_and_bp
'
)
{
option
.
yAxis
.
name
=
`fp+bp
${
this
.
$t
(
'
profiling.time
'
)}
(ms)`
;
this
.
tabsArr
[
1
].
noData
=
this
.
steps
.
length
?
false
:
true
;
}
else
if
(
type
===
'
tail
'
)
{
option
.
yAxis
.
name
=
`tail
${
this
.
$t
(
'
profiling.time
'
)}
(ms)`
;
this
.
tabsArr
[
2
].
noData
=
this
.
steps
.
length
?
false
:
true
;
}
this
.
initChart
(
option
,
id
);
}
else
{
this
.
steps
=
[];
this
.
selectedStep
=
''
;
}
}
},
(
error
)
=>
{
this
.
steps
=
[];
this
.
selectedStep
=
''
;
if
(
type
===
'
iteration_interval
'
)
{
this
.
tabsArr
[
0
].
noData
=
true
;
}
else
if
(
type
===
'
fp_and_bp
'
)
{
this
.
tabsArr
[
1
].
noData
=
true
;
}
else
if
(
type
===
'
tail
'
)
{
this
.
tabsArr
[
2
].
noData
=
true
;
}
},
);
},
setStep
(
step
=
0
)
{
this
.
steps
=
[];
this
.
steps
.
push
({
label
:
this
.
$t
(
'
profiling.showAverage
'
),
value
:
this
.
$t
(
'
profiling.showAverage
'
),
});
for
(
let
i
=
1
;
i
<=
step
;
i
++
)
{
this
.
steps
.
push
({
label
:
i
,
value
:
i
,
});
}
this
.
selectedStep
=
this
.
$t
(
'
profiling.showAverage
'
);
},
initChart
(
option
,
id
)
{
this
.
$nextTick
(()
=>
{
const
chart
=
echarts
.
init
(
document
.
getElementById
(
id
));
chart
.
setOption
(
option
,
true
);
this
.
charts
.
push
(
chart
);
});
},
resizeEchart
()
{
setTimeout
(()
=>
{
this
.
charts
.
forEach
((
val
)
=>
{
val
.
resize
();
});
},
300
);
},
queryTrainingTrace
(
step
)
{
const
params
=
{
dir
:
this
.
relativePath
,
type
:
step
,
device_id
:
this
.
deviceId
,
};
RequestService
.
queryTrainingTrace
(
params
).
then
(
(
res
)
=>
{
if
(
res
.
data
&&
res
.
data
.
training_trace_graph
&&
res
.
data
.
training_trace_graph
.
length
)
{
this
.
svg
.
noData
=
false
;
document
.
querySelector
(
'
#trace
'
).
style
.
height
=
`
${
res
.
data
.
training_trace_graph
.
length
*
this
.
svg
.
rowHeight
}
px`
;
this
.
svg
.
data
=
JSON
.
parse
(
JSON
.
stringify
(
res
.
data
.
training_trace_graph
),
);
this
.
removeTrace
();
setTimeout
(()
=>
{
this
.
dealTraceData
();
},
100
);
}
else
{
this
.
svg
.
data
=
[];
this
.
svg
.
noData
=
true
;
this
.
removeTrace
();
}
},
(
error
)
=>
{
this
.
svg
.
data
=
[];
this
.
svg
.
noData
=
true
;
this
.
removeTrace
();
},
);
},
dealTraceData
()
{
this
.
svg
.
totalWidth
=
document
.
querySelector
(
'
#trace
'
).
offsetWidth
-
this
.
svg
.
svgPadding
*
2
;
if
(
this
.
svg
.
data
[
0
]
&&
this
.
svg
.
data
[
0
].
length
)
{
const
svg
=
document
.
querySelector
(
'
#trace svg
'
);
this
.
svg
.
totalTime
=
this
.
svg
.
data
[
0
][
0
].
duration
;
if
(
this
.
svg
.
totalTime
)
{
this
.
svg
.
data
.
forEach
((
row
,
index
)
=>
{
if
(
row
&&
row
.
length
)
{
const
dashedLine
=
this
.
addDashedLine
(
index
);
svg
.
insertBefore
(
dashedLine
,
svg
.
querySelector
(
'
g
'
));
row
.
forEach
((
i
)
=>
{
if
(
i
.
duration
)
{
const
tempDom
=
i
.
name
?
this
.
createRect
(
i
,
index
)
:
this
.
createArrow
(
i
,
index
);
svg
.
insertBefore
(
tempDom
,
svg
.
querySelector
(
'
g
'
));
}
});
}
});
}
}
else
{
this
.
removeTrace
();
}
},
addDashedLine
(
index
)
{
const
x1
=
this
.
svg
.
svgPadding
;
const
x2
=
this
.
svg
.
svgPadding
+
this
.
svg
.
totalWidth
;
const
y
=
index
*
this
.
svg
.
rowHeight
;
const
line
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
line
.
setAttribute
(
'
x1
'
,
x1
);
line
.
setAttribute
(
'
y1
'
,
y
);
line
.
setAttribute
(
'
x2
'
,
x2
);
line
.
setAttribute
(
'
y2
'
,
y
);
line
.
setAttribute
(
'
style
'
,
'
stroke:#E2E2E2;stroke-width:1
'
);
line
.
setAttribute
(
'
stroke-dasharray
'
,
'
5 5
'
);
const
g
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
g
.
appendChild
(
line
);
return
g
;
},
createRect
(
data
,
rowIndex
)
{
const
color
=
this
.
svg
.
colorList
[
this
.
svg
.
colorIndex
++
%
4
];
const
height
=
40
;
const
width
=
(
data
.
duration
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
;
const
x1
=
(
data
.
start
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
+
this
.
svg
.
svgPadding
;
const
y1
=
rowIndex
*
this
.
svg
.
rowHeight
+
(
this
.
svg
.
rowHeight
-
height
)
/
2
;
const
g
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
const
gChild
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
const
rect
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
rect
'
);
rect
.
setAttribute
(
'
x
'
,
x1
);
rect
.
setAttribute
(
'
y
'
,
y1
);
rect
.
setAttribute
(
'
height
'
,
height
);
rect
.
setAttribute
(
'
width
'
,
width
);
rect
.
setAttribute
(
'
style
'
,
`fill:
${
color
[
1
]}
;stroke:
${
color
[
1
]}
;`
);
const
foreignObject
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
foreignObject
'
,
);
foreignObject
.
setAttribute
(
'
x
'
,
x1
);
foreignObject
.
setAttribute
(
'
y
'
,
y1
);
foreignObject
.
setAttribute
(
'
height
'
,
height
);
foreignObject
.
setAttribute
(
'
width
'
,
width
);
foreignObject
.
setAttribute
(
'
style
'
,
`overflow:hidden;text-align:center;text-overflow:ellipsis;`
+
`white-space:nowrap;font-size:12px;line-height:
${
height
}
px;color:
${
color
[
0
]}
`
,
);
foreignObject
.
textContent
=
`
${
data
.
name
}
:
${
data
.
duration
.
toFixed
(
4
)}
ms`
;
const
title
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
title
'
);
title
.
textContent
=
`
${
data
.
name
}
:
${
data
.
duration
.
toFixed
(
4
)}
ms`
;
gChild
.
appendChild
(
rect
);
gChild
.
appendChild
(
foreignObject
);
gChild
.
appendChild
(
title
);
g
.
appendChild
(
gChild
);
return
g
;
},
createArrow
(
data
,
rowIndex
)
{
const
width
=
(
data
.
duration
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
;
const
x1
=
(
data
.
start
/
this
.
svg
.
totalTime
)
*
this
.
svg
.
totalWidth
+
this
.
svg
.
markerPadding
+
this
.
svg
.
svgPadding
;
const
x2
=
x1
+
width
-
this
.
svg
.
markerPadding
*
2
;
const
y
=
rowIndex
*
this
.
svg
.
rowHeight
+
this
.
svg
.
rowHeight
/
2
;
const
g
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
g
'
);
const
line
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
line
.
setAttribute
(
'
x1
'
,
x1
);
line
.
setAttribute
(
'
y1
'
,
y
);
line
.
setAttribute
(
'
x2
'
,
x2
);
line
.
setAttribute
(
'
y2
'
,
y
);
line
.
setAttribute
(
'
style
'
,
'
stroke:#E6EBF5;stroke-width:1
'
);
line
.
setAttribute
(
'
marker-end
'
,
'
url(#marker_end)
'
);
line
.
setAttribute
(
'
marker-start
'
,
'
url(#marker_start)
'
);
const
text
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
text
'
);
text
.
textContent
=
`
${
data
.
duration
.
toFixed
(
4
)}
ms`
;
const
textWidth
=
this
.
getTextWidth
(
text
.
textContent
);
text
.
setAttribute
(
'
x
'
,
Math
.
max
(
0
,
(
x2
-
x1
)
/
2
+
x1
-
textWidth
/
2
));
text
.
setAttribute
(
'
y
'
,
y
-
6
);
text
.
setAttribute
(
'
font-size
'
,
12
);
text
.
setAttribute
(
'
fill
'
,
'
#6c7280
'
);
const
startLine
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
startLine
.
setAttribute
(
'
x1
'
,
x1
-
this
.
svg
.
markerPadding
);
startLine
.
setAttribute
(
'
y1
'
,
y
-
this
.
svg
.
rowHeight
/
4
);
startLine
.
setAttribute
(
'
x2
'
,
x1
-
this
.
svg
.
markerPadding
);
startLine
.
setAttribute
(
'
y2
'
,
y
+
this
.
svg
.
rowHeight
/
4
);
startLine
.
setAttribute
(
'
style
'
,
'
stroke:#E6EBF5;stroke-width:1
'
);
g
.
appendChild
(
startLine
);
const
endLine
=
document
.
createElementNS
(
this
.
svg
.
namespaceURI
,
'
line
'
);
endLine
.
setAttribute
(
'
x1
'
,
x1
+
width
-
this
.
svg
.
markerPadding
);
endLine
.
setAttribute
(
'
y1
'
,
y
-
this
.
svg
.
rowHeight
/
4
);
endLine
.
setAttribute
(
'
x2
'
,
x1
+
width
-
this
.
svg
.
markerPadding
);
endLine
.
setAttribute
(
'
y2
'
,
y
+
this
.
svg
.
rowHeight
/
4
);
endLine
.
setAttribute
(
'
style
'
,
'
stroke:#E6EBF5;stroke-width:1
'
);
g
.
appendChild
(
endLine
);
g
.
appendChild
(
line
);
g
.
appendChild
(
text
);
return
g
;
},
getTextWidth
(
text
)
{
const
body
=
document
.
querySelector
(
'
body
'
);
const
temp
=
document
.
createElement
(
'
span
'
);
temp
.
style
[
'
font-size
'
]
=
'
12px
'
;
temp
.
textContent
=
text
;
body
.
appendChild
(
temp
);
const
textWidth
=
temp
.
offsetWidth
;
body
.
removeChild
(
temp
);
return
textWidth
;
},
removeTrace
()
{
const
svgDom
=
document
.
querySelector
(
'
#trace svg
'
);
if
(
svgDom
)
{
const
gDoms
=
svgDom
.
children
;
if
(
gDoms
)
{
for
(
let
i
=
0
;
i
<
gDoms
.
length
;
i
++
)
{
if
(
gDoms
[
i
].
nodeName
===
'
g
'
)
{
svgDom
.
removeChild
(
gDoms
[
i
--
]);
}
}
}
}
},
resizeTrace
()
{
if
(
this
.
svg
.
resizeTimer
)
{
clearTimeout
(
this
.
svg
.
resizeTimer
);
}
this
.
svg
.
resizeTimer
=
setTimeout
(()
=>
{
this
.
removeTrace
();
this
.
dealTraceData
();
this
.
svg
.
resizeTimer
=
null
;
},
500
);
},
downloadSVG
()
{
const
svgDom
=
document
.
querySelector
(
'
svg
'
).
outerHTML
;
const
src
=
`data:image/svg+xml;base64,
${
window
.
btoa
(
unescape
(
encodeURIComponent
(
svgDom
)))}
`
;
const
a
=
document
.
createElement
(
'
a
'
);
a
.
href
=
src
;
a
.
download
=
new
Date
().
valueOf
();
a
.
click
();
},
},
destroyed
()
{
window
.
removeEventListener
(
'
resize
'
,
this
.
resizeTrace
,
false
);
window
.
removeEventListener
(
'
resize
'
,
this
.
resizeEchart
,
false
);
this
.
$bus
.
$off
(
'
resize
'
);
},
};
</
script
>
<
style
lang=
"scss"
>
.step-trace
{
width
:
100%
;
height
:
100%
;
.step-trace-title
{
padding
:
0
15px
;
font-size
:
16px
;
font-weight
:
bold
;
.pf-content-right
{
display
:
inline-block
;
margin-left
:
35px
;
label
{
margin-right
:
10px
;
}
}
.input-wrap
{
font-weight
:
normal
;
}
}
.pf-content-middle
{
padding
:
15px
15px
0
;
height
:
calc
(
100%
-
32px
);
#trace-container
{
width
:
100%
;
height
:
50%
;
border
:
1px
solid
#ccc
;
overflow
:
auto
;
.training-trace
{
position
:
relative
;
height
:
0
;
.download-button
{
display
:
none
;
position
:
absolute
;
width
:
12px
;
height
:
12px
;
right
:
10px
;
top
:
10px
;
cursor
:
pointer
;
background-image
:
url('../../assets/images/download.png')
;
}
}
}
.chart-wrap
{
float
:
left
;
height
:
calc
(
50%
-
20px
);
margin-top
:
20px
;
margin-right
:
15px
;
width
:
calc
(
33
.3%
-
10px
);
border
:
1px
solid
#ccc
;
padding
:
30px
;
border-radius
:
4px
;
overflow
:
auto
;
&
:last-child
{
margin-right
:
0
;
}
.chart
{
height
:
calc
(
100%
-
85px
);
min-height
:
180px
;
}
.title
{
margin
:
0
0
15px
20px
;
font-weight
:
bold
;
}
.rate-wrap
{
font-size
:
12px
;
padding-left
:
20px
;
&
>
div
{
display
:
inline-block
;
margin
:
0
15px
5px
0
;
color
:
#464950
;
span
{
margin-right
:
10px
;
color
:
#6c7280
;
}
}
}
}
}
.image-noData
{
width
:
100%
;
height
:
calc
(
100%
-
52px
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
flex-direction
:
column
;
p
{
font-size
:
16px
;
padding-top
:
10px
;
}
}
.image-noData.svg
{
height
:
100%
;
}
}
</
style
>
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录