Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MindSpore
mindinsight
提交
d73c66bd
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看板
提交
d73c66bd
编写于
6月 12, 2020
作者:
P
ph
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add profiling new page
上级
20c153de
变更
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
浏览文件 @
d73c66bd
<?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
浏览文件 @
d73c66bd
<?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
浏览文件 @
d73c66bd
<
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
浏览文件 @
d73c66bd
<!--
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
浏览文件 @
d73c66bd
<!--
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
浏览文件 @
d73c66bd
<!--
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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录