Commit 6b3e262f authored by 张牧越's avatar 张牧越

临边 联调

parent 94ef9805
...@@ -299,4 +299,53 @@ export function getSafeCapUseRecords(params) { ...@@ -299,4 +299,53 @@ export function getSafeCapUseRecords(params) {
} }
//fall-protection
export function getBuildingList() {
return request({
url: '/pweb/s/linbian/device/list',
method: 'get',
})
}
export function get24hourWarningData() {
return request({
url: '/pweb/s/linbian/day/statistics',
method: 'get',
})
}
export function getFallWarningList() {
return request({
url: '/pweb/s/linbian/today/statistics',
method: 'get',
})
}
export function getFallWarningStatus() {
return request({
url: '/pweb/s/linbian/statistics',
method: 'get',
})
}
export function getFallWarningTableList() {
return request({
url: '/pweb/s/linbian/warning',
method: 'get',
})
}
export function getDeviceCurrentStatus(params) {
return request({
url: '/pweb/s/linbian/device/realdata',
method: 'get',
params
})
}
<template> <template>
<div :style="{ zoom: zoom }"> <div>
<div class="nav-header"> <div class="nav-header">
<div class="left-nav"> <div class="left-nav">
<div class="weather"> <div class="weather">
...@@ -16,13 +16,33 @@ ...@@ -16,13 +16,33 @@
<div class="condition">{{ screenDetail.weather_condition }}</div> <div class="condition">{{ screenDetail.weather_condition }}</div>
</div> </div>
</div> </div>
<router-link <template v-for="(router, index) in leftNav">
v-for="(router, index) in leftNav" <router-link
:key="index" v-if="router.child && router.child.length == 0"
:to="{ path: router.menu_url, query: { token_code: token } }" :key="index"
> :to="{ path: router.menu_url, query: { token_code: token } }"
{{ router.right_name }}</router-link >
> {{ router.right_name }}</router-link
>
<el-dropdown v-else :key="index" placement="bottom">
<div class="parent-router">
{{ router.right_name }}
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="(child, cIndex) in router.child"
:key="cIndex"
>
<router-link
:to="{ path: child.menu_url, query: { token_code: token } }"
>
{{ child.right_name }}</router-link
>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</div> </div>
<div class="mid-title"> <div class="mid-title">
<el-tooltip <el-tooltip
...@@ -35,13 +55,15 @@ ...@@ -35,13 +55,15 @@
</el-tooltip> </el-tooltip>
</div> </div>
<div class="right-nav"> <div class="right-nav">
<router-link <template v-for="(router, index) in rightNav">
v-for="(router, index) in rightNav" <router-link
:key="index" :key="index"
:to="{ path: router.menu_url, query: { token_code: token } }" :to="{ path: router.menu_url, query: { token_code: token } }"
> >
{{ router.right_name }}</router-link {{ router.right_name }}</router-link
> >
</template>
<div class="clock"> <div class="clock">
<div class="time"> <div class="time">
{{ time }} {{ time }}
...@@ -208,7 +230,9 @@ export default { ...@@ -208,7 +230,9 @@ export default {
} }
} }
} }
a { a,
.parent-router {
cursor: pointer;
min-width: 66px; min-width: 66px;
text-align: center; text-align: center;
display: inline-block; display: inline-block;
...@@ -221,6 +245,9 @@ a { ...@@ -221,6 +245,9 @@ a {
line-height: 16px; line-height: 16px;
font-size: 16px; font-size: 16px;
background-image: linear-gradient(#0d3353, #183e75); background-image: linear-gradient(#0d3353, #183e75);
&:last-child {
margin-right: 0;
}
&:hover { &:hover {
color: #fff; color: #fff;
border-color: #27acfb; border-color: #27acfb;
...@@ -240,6 +267,7 @@ a { ...@@ -240,6 +267,7 @@ a {
} }
} }
} }
.router-link-exact-active.router-link-active { .router-link-exact-active.router-link-active {
color: #fff; color: #fff;
border-color: #27acfb; border-color: #27acfb;
...@@ -282,4 +310,21 @@ a { ...@@ -282,4 +310,21 @@ a {
margin-right: 6px; margin-right: 6px;
} }
} }
.el-dropdown-menu {
padding: 0;
border: none;
margin: 0;
background: none;
a {
margin-right: 0;
width: 80px;
}
.el-dropdown-menu__item:focus,
.el-dropdown-menu__item:not(.is-disabled):hover {
background: transparent !important;
}
}
.el-popper[x-placement^="bottom"] .popper__arrow::after {
display: none;
}
</style> </style>
\ No newline at end of file
...@@ -192,23 +192,29 @@ ...@@ -192,23 +192,29 @@
</div> </div>
</div> </div>
</div> </div>
<div class="steps"> <div
<el-steps :active="progressDetail.step" finish-status="success"> class="steps"
<el-step @mouseleave="mouseLeaveEvent"
v-for="(progress, index) in progressDetail.progress_data" @mouseover="mouseOverEvent"
:key="index" >
> <div id="step-scroll">
<template #title> <el-steps :active="progressDetail.step" finish-status="success">
<div class="step-title"> <el-step
{{ progress.over_date }} v-for="(progress, index) in progressDetail.progress_data"
<div class="step-status">{{ progress.status_text }}</div> :key="index"
</div> >
</template> <template #title>
<template #description> <div class="step-title">
<div class="step-description">{{ progress.name }}</div> {{ progress.over_date }}
</template> <div class="step-status">{{ progress.status_text }}</div>
</el-step> </div>
</el-steps> </template>
<template #description>
<div class="step-description">{{ progress.name }}</div>
</template>
</el-step>
</el-steps>
</div>
</div> </div>
</div> </div>
</ShadowContainer> </ShadowContainer>
...@@ -464,6 +470,12 @@ export default { ...@@ -464,6 +470,12 @@ export default {
getProgressDetail().then((res) => { getProgressDetail().then((res) => {
if (res.status == 200) { if (res.status == 200) {
this.progressDetail = res.data; this.progressDetail = res.data;
if (res.data.progress_data.length > 6) {
document.getElementById("step-scroll").style.width =
2 * res.data.progress_data.length + "rem";
document.getElementById("step-scroll").style.animationDuration =
res.data.progress_data.length / 2 + "s";
}
this.$nextTick(() => { this.$nextTick(() => {
const lines = document.getElementsByClassName( const lines = document.getElementsByClassName(
"el-step__line-inner" "el-step__line-inner"
...@@ -481,6 +493,8 @@ export default { ...@@ -481,6 +493,8 @@ export default {
this.renderQualityChart(); this.renderQualityChart();
}); });
}, },
mouseLeaveEvent() {},
mouseOverEvent() {},
}, },
mounted() { mounted() {
this.getBaseData(); this.getBaseData();
...@@ -635,6 +649,7 @@ table { ...@@ -635,6 +649,7 @@ table {
} }
.steps { .steps {
padding-top: 40px; padding-top: 40px;
overflow-x: hidden;
} }
.step-title { .step-title {
position: relative; position: relative;
...@@ -803,4 +818,17 @@ table { ...@@ -803,4 +818,17 @@ table {
} }
} }
} }
@keyframes scrolls {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(calc(-100% - 13rem));
}
}
.step-scroll {
animation-name: scrolls;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
</style> </style>
\ No newline at end of file
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
alt="" alt=""
/> />
{{ warns.area_name }} {{ warns.area_name }}
({{ warns.serial_no }}) ({{ warns.device_no }})
</div> </div>
</div> </div>
<div class="status">状态:{{ warns.status_text }}</div> <div class="status">状态:{{ warns.status_text }}</div>
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
</ShadowContainer> </ShadowContainer>
</div> </div>
<div class="right-container"> <div class="right-container">
<Title type="long" text="今日统计"></Title> <Title type="long" text="整改统计"></Title>
<ShadowContainer> <ShadowContainer>
<div class="total-chart"> <div class="total-chart">
<div class="chart-container"> <div class="chart-container">
...@@ -522,7 +522,7 @@ export default { ...@@ -522,7 +522,7 @@ export default {
.warp { .warp {
width: 100%; width: 100%;
height: 660px; height: 660px;
overflow: hidden; overflow-y: auto;
} }
.person-check { .person-check {
padding: 14px; padding: 14px;
......
<template>
<div id="fall-protection">
<div class="side-area">
<Title text="工地区域"></Title>
<ShadowContainer>
<div id="level-description">
<div
class="levels"
v-for="(building, index) in buildingList"
:key="index"
>
<div class="level-title" @click="showDeviceStatus(building)">
<img src="@/assets/fallprotection/building-2.png" alt="" />
{{ building.area_name }}
<span>{{ building.total }}</span>
</div>
<div v-show="building.showDevice">
<div
class="level-info"
v-for="(device, i) in building.device_list"
:key="i"
@click="showDeviceInfo(device)"
>
<img src="@/assets/fallprotection/device.png" alt="" />
{{ device.device_no }}
<span :style="{ color: device.status == 1 ? '#26D591' : '' }">{{
device.status_name
}}</span>
</div>
</div>
</div>
</div>
</ShadowContainer>
<Title text="近24小时报警分析"></Title>
<ShadowContainer>
<div id="today-warning-chart" ref="today24hourWarningChart"></div>
</ShadowContainer>
</div>
<div class="mid-area">
<div class="buildingwindow" v-if="buildingWindowVisible">
<div class="building-description">
<img src="@/assets/fallprotection/building.png" alt="" />
{{ windowData.device_no }}
</div>
<div class="building-info">
<div
class="building-info-text"
v-for="(info, index) in windowData.alarm_list"
:key="index"
>
{{ info.alarm_name }}{{ info.describe }}
</div>
</div>
<div class="recent-open">
最后更新:{{ windowData.latest_record_time }}
</div>
</div>
<div class="swagger-container">
<img class="lt" src="@/assets/lt.png" alt="" />
<img class="rt" src="@/assets/rt.png" alt="" />
<img class="rb" src="@/assets/rb.png" alt="" />
<img class="lb" src="@/assets/lb.png" alt="" />
<div id="amap-container"></div>
</div>
</div>
<div class="side-area">
<Title text="今日报警统计"></Title>
<ShadowContainer>
<div class="warning-scroll">
<div
class="warning-count"
v-for="(warn, index) in fallWarningList"
:key="index"
>
<img src="@/assets/fallprotection/rect.png" alt="" />
<div class="warning-info">
<div class="warning-num">{{ warn.num }}</div>
<div class="warning-text">{{ warn.alarm_type_text }}</div>
</div>
</div>
</div>
</ShadowContainer>
<Title text="报警信息"></Title>
<ShadowContainer>
<div id="warning-info">
<div>
<div class="warning-info-text">今日报警</div>
<div class="warning-info-num">
{{ fallwarningStatus.today_num }}
</div>
<div class="warning-info-img">
<img src="@/assets/fallprotection/today.png" alt="" />
</div>
</div>
<div>
<div class="warning-info-text">累计未处理报警</div>
<div class="warning-info-num" style="color: #ffc74c">
{{ fallwarningStatus.un_handle_num }}
</div>
<div class="warning-info-img">
<img src="@/assets/fallprotection/total-no.png" alt="" />
</div>
</div>
<div>
<div class="warning-info-text">累计履责率</div>
<div class="warning-info-num" style="color: #22c3ed">
{{ fallwarningStatus.handle_rate
}}<span style="font-size: 14px; margin-left: 12px">%</span>
</div>
<div class="warning-info-img">
<img src="@/assets/fallprotection/percent.png" alt="" />
</div>
</div>
</div>
<div class="warning-table">
<div class="warning-table-header">
<div class="warning-table-run-time">运行时间</div>
<div class="warning-table-device">设备</div>
<div class="warning-table-info">告警信息</div>
<div class="warning-table-status">处理状态</div>
</div>
<div class="warning-table-list">
<div
class="warning-table-column"
v-for="(column, index) in fallwarningTableData"
:key="index"
>
<div class="warning-table-run-time">{{ column.log_time }}</div>
<el-tooltip
class="item"
effect="dark"
:content="column.device_no"
placement="top"
>
<div class="warning-table-device">{{ column.device_no }}</div>
</el-tooltip>
<el-tooltip
class="item"
effect="dark"
:content="column.alarm_content"
placement="top"
>
<div class="warning-table-info">
{{ column.alarm_content }}
</div>
</el-tooltip>
<div
class="warning-table-status"
:style="{ color: column.status == 1 ? '#FFC74C' : '#22C3ED' }"
>
{{ column.status_text }}
</div>
</div>
</div>
</div>
</ShadowContainer>
</div>
</div>
</template>
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
import {
getBaseInfo,
getBuildingList,
get24hourWarningData,
getFallWarningList,
getFallWarningStatus,
getFallWarningTableList,
getDeviceCurrentStatus,
} from "@/api/index";
import buildingIcon from "@/assets/fallprotection/building-marker.png";
export default {
name: "FallProtection",
data() {
return {
levelList: [],
AMap: null,
map: null,
mapCenter: [],
buildingList: [],
warning24hourData: [],
fallWarningList: [],
fallwarningStatus: {
today_num: 0,
un_handle_num: 0,
handle_rate: 0,
},
fallwarningTableData: [],
buildingWindowVisible: false,
windowData: {},
};
},
methods: {
initMap() {
AMapLoader.load({
key: "fc00c5b70f67a8414344682f217d4b6e",
version: "1.4.15",
plugins: ["AMap.MarkerClusterer", "AMap.DistrictSearch"],
})
.then((AMap) => {
// 初始化地图
this.AMap = AMap;
this.map = new AMap.Map("amap-container", {
viewMode: "2D",
zoom: 20,
center: this.mapCenter,
resizeEnable: true,
mapStyle: "amap://styles/darkblue",
});
this.map.on("click", (e) => {
this.buildingWindowVisible = false;
});
this.getBuildingList();
})
.catch((e) => {
console.log(e);
});
},
setMarker() {
this.buildingList.map((building) => {
building.device_list.map((device) => {
const mapMarkers = new this.AMap.Marker({
icon: buildingIcon,
position: [device.longitude, device.latitude],
offset: new this.AMap.Pixel(-25, -25),
extData: {
markerData: device,
},
});
mapMarkers.setMap(this.map);
mapMarkers.on("click", (e) => {
const markData = e.target.getExtData().markerData;
this.map.setCenter([markData.longitude, markData.latitude]);
this.$nextTick(() => {
this.showDeviceInfo(markData);
});
});
});
});
},
getBuildingList() {
getBuildingList().then((res) => {
this.buildingList = res.data;
this.setMarker();
});
},
showDeviceStatus(item) {
if (item.showDevice) {
item.showDevice = false;
} else {
item.showDevice = true;
}
this.$forceUpdate();
},
showDeviceInfo(device) {
this.map.setCenter([device.longitude, device.latitude]);
getDeviceCurrentStatus({ device_id: device.id }).then((res) => {
this.windowData = { ...device, ...res.data };
console.log(this.windowData);
this.$nextTick(() => {
this.buildingWindowVisible = true;
});
});
},
render24hourWarningChart() {
const xAxisData = this.warning24hourData.map((item) => {
return item.hour;
});
const yAxisData = this.warning24hourData.map((item) => {
return item.num;
});
const chart = this.$echarts.init(this.$refs.today24hourWarningChart);
const option = {
color: ["#ff4544"],
grid: {
top: 30,
bottom: 30,
left: 30,
right: 30,
},
tooltip: {
trigger: "axis",
},
xAxis: {
type: "category",
data: xAxisData,
axisTick: { show: false },
axisLabel: {
fontSize: "12",
color: "#c6def9",
},
},
yAxis: {
type: "value",
axisLabel: {
fontSize: "12",
color: "#c6def9",
},
splitNumber: 4,
splitLine: {
lineStyle: {
color: "rgba(95, 120, 144, 0.4)",
},
},
minInterval: 1,
},
series: [
{
data: yAxisData,
type: "line",
smooth: true,
areaStyle: {
color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#ff4544",
},
{
offset: 1,
color: "rgba(0,0,0,0)",
},
]),
},
},
],
};
chart.setOption(option);
this.$nextTick(() => {
// 解决echarts图表放大溢出父容器
const resizeOb = new ResizeObserver((entries) => {
for (const entry of entries) {
this.$echarts.getInstanceByDom(entry.target).resize();
}
});
resizeOb.observe(document.getElementById("today-warning-chart"));
});
},
},
mounted() {
getBaseInfo().then((res) => {
this.mapCenter = [res.data.longitude, res.data.latitude];
this.$nextTick(() => {
this.initMap();
});
});
get24hourWarningData().then((res) => {
this.warning24hourData = res.data;
this.$nextTick(() => {
this.render24hourWarningChart();
});
});
getFallWarningList().then((res) => {
this.fallWarningList = res.data;
});
getFallWarningStatus().then((res) => {
this.fallwarningStatus = res.data;
});
getFallWarningTableList().then((res) => {
this.fallwarningTableData = res.data.data;
});
},
};
</script>
<style lang="less" scoped>
#fall-protection {
padding: 12px 20px;
display: flex;
justify-content: space-between;
}
.side-area {
width: 25%;
}
.mid-area {
width: calc(50% - 40px);
margin: 0 20px;
position: relative;
}
.swagger-container {
position: relative;
.container-img {
width: 100%;
height: 520px;
vertical-align: top;
}
.lt,
.lb,
.rb,
.rt {
position: absolute;
width: 20px;
height: 20px;
}
.lt {
top: -2px;
left: -2px;
}
.lb {
left: -2px;
bottom: -2px;
}
.rb {
right: -2px;
bottom: -2px;
}
.rt {
right: -2px;
top: -2px;
}
}
#amap-container {
width: 100%;
height: 100%;
}
.swagger-container {
width: 100%;
height: 950px;
}
#level-description {
height: 340px;
overflow-y: scroll;
.levels {
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
.level-title {
line-height: 22px;
img {
width: 22px;
vertical-align: top;
}
background: rgba(44, 96, 162, 0.2);
font-size: 14px;
color: #c6def9;
padding: 10px 12px;
cursor: pointer;
span {
float: right;
}
}
.level-info {
line-height: 22px;
img {
width: 22px;
vertical-align: top;
}
background: rgba(44, 96, 162, 0.1);
font-size: 14px;
color: #c6def9;
padding: 10px 12px;
border-top: 1px solid rgba(122, 143, 173, 0.2);
cursor: pointer;
span {
float: right;
}
}
}
}
#today-warning-chart {
height: 400px;
}
.warning-count {
display: inline-block;
vertical-align: top;
width: 49%;
position: relative;
margin-right: 2%;
&:nth-child(2n) {
margin-right: 0;
}
img {
vertical-align: top;
width: 100%;
}
.warning-info {
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
text-align: center;
.warning-num {
font-size: 24px;
color: #ff6151;
}
.warning-text {
font-size: 14px;
color: #27acfb;
}
}
}
#warning-info {
display: flex;
justify-content: space-between;
text-align: center;
.warning-info-text {
padding-top: 20px;
font-size: 16px;
color: #ffffff;
line-height: 18px;
}
.warning-info-num {
font-size: 24px;
color: #ff6151;
}
.warning-info-img {
position: relative;
transform: translateY(-60px);
margin-bottom: -60px;
img {
width: 80%;
margin: 0 auto;
}
}
}
.warning-table {
font-size: 14px;
text-align: center;
color: #c6def9;
.warning-table-list {
height: 200px;
overflow-y: auto;
}
.warning-table-header {
background: rgba(44, 96, 162, 0.2);
display: flex;
padding: 12px 0;
}
.warning-table-column {
padding: 12px 0;
background: rgba(44, 96, 162, 0.05);
display: flex;
&:nth-child(2n) {
background: rgba(44, 96, 162, 0.1);
}
}
.warning-table-run-time {
width: 40%;
}
.warning-table-device,
.warning-table-info {
width: 20%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.warning-table-status {
width: 20%;
}
}
.warning-scroll {
height: 370px;
overflow-y: auto;
}
.buildingwindow {
width: 460px;
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-120%);
background: url("@/assets/safecap/cap-window-bg.png") no-repeat;
background-size: 100% 100%;
padding: 20px;
z-index: 1000;
}
.recent-open,
.building-info-text {
font-size: 16px;
color: #c6def9;
line-height: 45px;
}
.building-info {
display: flex;
flex-wrap: wrap;
}
.building-info-text {
width: 50%;
}
.building-description {
line-height: 32px;
font-size: 18px;
color: #c6def9;
padding-bottom: 16px;
border-bottom: 1px solid #7a8fad;
img {
width: 32px;
vertical-align: top;
}
.name {
float: right;
}
.online-status.online {
color: rgba(62, 236, 111, 1);
}
}
::v-deep .amap-icon {
img {
width: 50px;
height: 50px;
}
}
</style>
\ No newline at end of file
...@@ -119,13 +119,13 @@ ...@@ -119,13 +119,13 @@
</ShadowContainer> </ShadowContainer>
<Title text="人员地域分布"></Title> <Title text="人员地域分布"></Title>
<ShadowContainer> <ShadowContainer>
<div class="chart-container"> <div class="chart-container" id="reset-area2">
<img class="custom-chart" src="@/assets/chart.png" /> <img class="custom-chart" src="@/assets/chart.png" />
<div ref="areaChart" id="area-chart"></div> <div ref="areaChart" id="area-chart"></div>
</div> </div>
</ShadowContainer> </ShadowContainer>
</div> </div>
<div class="rt-area"> <div class="rt-area" id="reset-area1">
<Title type="long" text="一周考勤"></Title> <Title type="long" text="一周考勤"></Title>
<ShadowContainer> <ShadowContainer>
<div class="chart-title">近一周考勤</div> <div class="chart-title">近一周考勤</div>
...@@ -603,6 +603,13 @@ export default { ...@@ -603,6 +603,13 @@ export default {
); );
}); });
}, },
resetSize(obj) {
let screenWidth =
document.body.clientWidth || document.documentElement.clientWidth;
let defWidth = 1920;
let xScale = screenWidth / defWidth;
obj.style.zoom = 1 / xScale;
},
}, },
mounted() { mounted() {
this.getTime(); this.getTime();
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
" "
alt="" alt=""
/> />
{{ windowData.device_sn }} / {{ windowData.device_no }} /
<span <span
:class="[ :class="[
'online-status', 'online-status',
...@@ -73,10 +73,10 @@ ...@@ -73,10 +73,10 @@
<el-tooltip <el-tooltip
class="item" class="item"
effect="dark" effect="dark"
:content="warn.alarm_content" :content="`${warn.alarm_content}(${warn.device_no})`"
> >
<div class="overflowed"> <div class="overflowed">
{{ warn.alarm_content }} {{ warn.alarm_content }}({{ warn.device_no }})
</div> </div>
</el-tooltip> </el-tooltip>
</div> </div>
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
" "
alt="" alt=""
/> />
{{ monitor.device_sn }} {{ monitor.device_no }}
<span <span
:style="{ :style="{
color: monitor.is_online_text == '在线' ? '#3eec6f' : '', color: monitor.is_online_text == '在线' ? '#3eec6f' : '',
......
...@@ -9,7 +9,8 @@ import Tower from "@/components/tower/Index" ...@@ -9,7 +9,8 @@ import Tower from "@/components/tower/Index"
import Noise from "@/components/noise/Index" import Noise from "@/components/noise/Index"
import Safecap from "@/components/safecap/Index" import Safecap from "@/components/safecap/Index"
import Management from "@/components/management/Index" import Management from "@/components/management/Index"
import FallProtection from "@/components/fallprotection/Index"
import UnloadingPlatform from "@/components/unloadingplatform/Index"
Vue.use(VueRouter) Vue.use(VueRouter)
...@@ -26,8 +27,11 @@ const router = new VueRouter({ ...@@ -26,8 +27,11 @@ const router = new VueRouter({
{ path: 'tower', component: Tower }, { path: 'tower', component: Tower },
{ path: 'noise', component: Noise }, { path: 'noise', component: Noise },
{ path: 'safecap', component: Safecap }, { path: 'safecap', component: Safecap },
{ path: 'management', component: Management }, { path: 'concrete', component: Management },
{ path: 'fallprotection', component: FallProtection },
{ path: 'unloadingplatform', component: UnloadingPlatform },
] ]
}, },
] ]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment