Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
DC-TOM
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
刘照晖
DC-TOM
Commits
ebcf087e
Commit
ebcf087e
authored
May 30, 2025
by
liuzhaoh
Browse files
Options
Browse Files
Download
Plain Diff
解决冲突
parents
cc98dce3
dc39c40b
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
800 additions
and
452 deletions
+800
-452
index.vue
src/components/commonTable/index.vue
+3
-0
bagMonitor.js
src/request/api/bagMonitor.js
+24
-0
index.js
src/router/index.js
+6
-0
routePermission.js
src/router/routePermission.js
+1
-14
chart.js
src/utils/chart.js
+6
-2
AboutView.vue
src/views/AboutView/AboutView.vue
+14
-1
myAgency.vue
src/views/closeManage/myAgency.vue
+2
-2
map-svg.vue
src/views/dashboard/components/map-svg.vue
+1
-0
bag-monitoring.vue
src/views/dustMonitoring/bag-monitoring.vue
+537
-365
healthyProgress.vue
src/views/dustMonitoring/components/healthyProgress.vue
+1
-1
warn.vue
src/views/dustMonitoring/components/warn.vue
+7
-4
index.vue
src/views/dustMonitoring/index.vue
+18
-3
addDustCollectorDialog.vue
src/views/dustOverview/components/addDustCollectorDialog.vue
+40
-29
roomSettingDialog.vue
src/views/dustOverview/components/roomSettingDialog.vue
+1
-0
valveSettingDialog.vue
src/views/dustOverview/components/valveSettingDialog.vue
+71
-14
index.vue
src/views/dustOverview/index.vue
+68
-17
No files found.
src/components/commonTable/index.vue
View file @
ebcf087e
...
...
@@ -4,6 +4,7 @@
ref=
"tableRef"
:data=
"currentPageData"
v-bind=
"$attrs"
:resizable=
"false"
border
:height=
"tableHeight"
>
...
...
@@ -221,6 +222,8 @@ watch(
width
:
100%
!
important
;
}
.el-table__header
th
{
user-select
:
none
;
}
.el-table
{
// 设置表头样式
th
.el-table__cell
{
...
...
src/request/api/bagMonitor.js
0 → 100644
View file @
ebcf087e
import
request
from
"../index.js"
;
export
function
getMonitorRealtime
(
params
)
{
return
request
({
url
:
"/api/duster/monitor/chamber"
,
method
:
"get"
,
params
,
});
}
export
function
getMonitorToday
(
params
)
{
return
request
({
url
:
"/api/duster/monitor/curve"
,
method
:
"get"
,
params
,
});
}
export
function
getMonitorList
(
params
)
{
return
request
({
url
:
"/api/duster/monitor/list"
,
method
:
"get"
,
params
,
});
}
src/router/index.js
View file @
ebcf087e
...
...
@@ -4,6 +4,12 @@ import Layout from '../layout/index.vue'
// 检查路由是否在授权菜单中的函数
const
checkRoutePermission
=
(
route
,
menuList
)
=>
{
// bag-monitor 路由不需要权限验证
if
(
route
.
path
===
'/bag-monitor'
)
{
return
true
;
}
// 检查当前路由
const
hasPermission
=
menuList
.
some
(
menu
=>
menu
.
name
===
route
.
meta
?.
title
&&
...
...
src/router/routePermission.js
View file @
ebcf087e
...
...
@@ -5,7 +5,7 @@ import NProgress from 'nprogress'
import
'nprogress/nprogress.css'
// progress bar style
NProgress
.
configure
({
showSpinner
:
false
})
// NProgress Configuration
const
whiteList
=
[
'/login'
,
'/auth-redirect'
]
// no redirect whitelist
const
whiteList
=
[
'/login'
,
'/auth-redirect'
,
'/bag-monitor'
]
// no redirect whitelist
router
.
beforeEach
(
async
(
to
,
from
,
next
)
=>
{
const
store
=
useUsersStore
()
const
hasToken
=
getToken
(
'TOKEN'
)
...
...
@@ -50,19 +50,6 @@ router.beforeEach(async (to, from, next) => {
}
}
else
{
try
{
// debugger
// get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
// const roles = await store.getInfo()
// generate accessible routes map based on roles
// const menuLimitsObj = await store.menuLimitsObj
// const accessRoutes = await store.dispatch('permission/generateRoutes', { roles, menuLimitsObj })
// console.log("accessRoutes",accessRoutes)
// dynamically add accessible routes
// router.addRoutes(accessRoutes)
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
// next({ ...to, replace: true })
next
()
}
catch
(
error
)
{
console
.
log
(
error
)
...
...
src/utils/chart.js
View file @
ebcf087e
...
...
@@ -132,6 +132,7 @@ export const getBagMonitoringChartOption = (xData = [], seriesData = []) => ({
top
:
"3%"
,
containLabel
:
true
,
},
// x轴的文字去除年月日
xAxis
:
{
type
:
"category"
,
boundaryGap
:
false
,
...
...
@@ -145,6 +146,9 @@ export const getBagMonitoringChartOption = (xData = [], seriesData = []) => ({
fontSize
:
12
,
interval
:
"auto"
,
rotate
:
0
,
formatter
:
function
(
value
)
{
return
value
.
split
(
' '
)[
1
];
}
},
axisTick
:
{
show
:
false
,
...
...
@@ -154,8 +158,8 @@ export const getBagMonitoringChartOption = (xData = [], seriesData = []) => ({
yAxis
:
{
type
:
"value"
,
min
:
0
,
max
:
600
,
interval
:
200
,
//
max: 600,
//
interval: 200,
axisLabel
:
{
color
:
"rgba(0,0,0,0.6)"
,
formatter
:
"{value}"
,
...
...
src/views/AboutView/AboutView.vue
View file @
ebcf087e
...
...
@@ -150,6 +150,8 @@ import { useRoute, useRouter } from "vue-router";
import
{
getDataFun
,
postDataJSON
}
from
"@/request/method.js"
;
import
moment
from
"moment"
;
const
route
=
useRoute
();
const
router
=
useRouter
();
const
formInline
=
ref
({
eventName
:
""
,
...
...
@@ -157,9 +159,20 @@ const formInline = ref({
dusterName
:
""
,
deviceType
:
""
,
suspendFlag
:
"2"
,
date
:
""
,
date
:
route
.
query
.
startTime
?
[
moment
(
new
Date
(
route
.
query
.
startTime
).
getTime
()).
format
(
"YYYY-MM-DD HH:mm:ss"
),
moment
(
new
Date
(
route
.
query
.
endTime
).
getTime
()).
format
(
"YYYY-MM-DD HH:mm:ss"
),
]
:
[],
});
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
20
);
const
equDialog
=
ref
(
false
);
...
...
src/views/closeManage/myAgency.vue
View file @
ebcf087e
...
...
@@ -46,8 +46,8 @@
/>
</el-form-item>
<el-form-item>
<el-button
class=
"default-btn"
@
click=
"onSearch"
>
搜索
</el-button>
<el-button
type=
"primary"
class=
"default-btn"
@
click=
"onReset"
>
重置
</el-button>
<el-button
type=
"primary"
class=
"default-btn"
@
click=
"onSearch"
>
搜索
</el-button>
<el-button
class=
"default-btn"
@
click=
"onReset"
>
重置
</el-button>
</el-form-item>
</el-form>
<div
class=
"table-box"
>
...
...
src/views/dashboard/components/map-svg.vue
View file @
ebcf087e
...
...
@@ -46,6 +46,7 @@
<span
class=
"label"
>
描述:
</span>
<div
class=
"value"
>
<span
class=
"value-item"
v-for=
"(item, index) in spot.description"
:key=
"index"
>
{{
item
}}
</span>
<span
class=
"value-item"
v-if=
"spot.description.length === 0"
>
暂无
</span>
</div>
</div>
...
...
src/views/dustMonitoring/bag-monitoring.vue
View file @
ebcf087e
...
...
@@ -2,432 +2,552 @@
<div
class=
"page-container bag-monitoring-container"
>
<div
class=
"header"
>
<div
class=
"select-container"
>
<span>
检测仪器11
</span>
<el-select
v-model=
"selectedDevice"
placeholder=
"1#布袋检测仪"
>
<el-option
label=
"1#布袋检测仪"
value=
"1"
></el-option>
<span>
检测仪器
</span>
<el-select
v-model=
"selectedDevice"
placeholder=
"1#布袋检测仪"
@
change=
"handleDeviceChange"
>
<el-option
v-for=
"item in deviceList"
:key=
"item.deviceNo"
:label=
"item.deviceName"
:value=
"item.deviceNo"
></el-option>
</el-select>
</div>
<div
class=
"title"
>
BME布袋监测
</div>
<div
class=
"title"
>
BME布袋监测
-
{{
dusterName
}}
</div>
</div>
<div
class=
"chart-container"
>
<div
class=
"chart-wrapper"
ref=
"chartRef"
></div>
<div
class=
"data-panel"
:class=
"
{ 'collapsed': !isPanelOpen }">
<div
class=
"panel-toggle"
:class=
"
{ 'collapsed-icon': !isPanelOpen, 'expanded-icon': isPanelOpen }" @click="togglePanel">
<el-icon
v-if=
"isPanelOpen"
><ArrowRightBold
/></el-icon>
<el-icon
v-if=
"!isPanelOpen"
><ArrowLeftBold
/></el-icon>
<div
class=
"chart-wrapper no-data"
v-if=
"chartData.xData.length === 0"
>
<div
class=
"no-data-text"
>
暂无数据
</div>
</div>
</div>
<div
class=
"data-panel"
:class=
"
{ collapsed: !isPanelOpen }">
<div
class=
"panel-toggle"
:class=
"
{
'collapsed-icon': !isPanelOpen,
'expanded-icon': isPanelOpen,
}"
@click="togglePanel"
>
<el-icon
v-if=
"isPanelOpen"
><ArrowRightBold
/></el-icon>
<el-icon
v-if=
"!isPanelOpen"
><ArrowLeftBold
/></el-icon>
</div>
<div
class=
"data-box"
>
<div
class=
"data-item"
>
<div>
时间:
{{
currentData
.
time
}}
</div>
<div>
仓室:
{{
currentData
.
compartNo
}}
</div>
<div>
反吹:
{{
currentData
.
blowBack
}}
</div>
</div>
<div
class=
"data-box"
>
<div
class=
"data-item"
>
<div>
时间:
{{
currentData
.
time
}}
</div>
<div>
仓室:
{{
currentData
.
chamber
}}
</div>
<div>
反吹:
{{
currentData
.
backwash
}}
</div>
</div>
<div
class=
"data-item"
>
<div>
DI3:
{{
currentData
.
di3
}}
</div>
<div>
排:
{{
currentData
.
row
}}
</div>
<div>
实时值:
{{
currentData
.
realValue
}}
</div>
</div>
<div
class=
"data-item"
>
<div>
基线值:
{{
currentData
.
baseline
}}
</div>
<div>
Delay:
{{
currentData
.
delay
}}
</div>
<div>
totaltime:
{{
currentData
.
totaltime
}}
</div>
</div>
<div
class=
"data-item"
>
<div>
{{
currentData
.
next
}}
</div>
<div>
valve:
{{
currentData
.
valve
}}
</div>
<div>
DI_TIME=
{{
currentData
.
di_time
}}
</div>
</div>
<div
class=
"data-item"
>
<div>
false:
{{
currentData
.
false
}}
</div>
<div>
peak=
{{
currentData
.
peak
}}
</div>
</div>
<div
class=
"data-item"
>
<div>
DI3:
{{
currentData
.
di3
}}
</div>
<div>
排:
{{
currentData
.
row
}}
</div>
<div>
实时值:
{{
currentData
.
realData
}}
</div>
</div>
<div
class=
"data-box second-box"
>
<div>
时间:
{{
secondaryData
.
time
}}
, 仓室:
{{
secondaryData
.
chamber
}}
, 反吹:
{{
secondaryData
.
backwash
}}
, 谷值:
{{
secondaryData
.
valleyValue
}}
, 仓室状态阀门
{{
secondaryData
.
status
}}
False
{{
secondaryData
.
falseVal
}}
</div>
<div
class=
"data-item"
>
<div>
基线值:
{{
currentData
.
baseline
}}
</div>
<div>
Delay:
{{
currentData
.
delay
}}
</div>
<div>
totaltime:
{{
currentData
.
totalTime
}}
</div>
</div>
<div
class=
"data-item"
>
<div>
next:
{{
currentData
.
next
}}
</div>
<div>
valve:
{{
currentData
.
valveNo
}}
</div>
<div>
DI_TIME=
{{
currentData
.
diTime
}}
</div>
</div>
<div
class=
"data-item"
>
<div>
peak=
{{
currentData
.
peak
}}
</div>
</div>
</div>
<!--
<div
class=
"data-box second-box"
>
<div>
时间:
{{
secondaryData
.
time
}}
, 仓室:
{{
secondaryData
.
chamber
}}
, 反吹:
{{
secondaryData
.
backwash
}}
, 谷值:
{{
secondaryData
.
valleyValue
}}
, 仓室状态阀门
{{
secondaryData
.
status
}}
False
{{
secondaryData
.
falseVal
}}
</div>
</div>
-->
</div>
<div
class=
"data-table"
>
<div
class=
"data-table"
>
<div
class=
"time-controls"
>
<el-button
@
click=
"navigateBackward"
>
<el-icon><ArrowLeftBold
/>
向前
</el-icon>
</el-button>
<el-button
@
click=
"navigateForward"
>
<el-icon><ArrowRightBold
/>
向后
</el-icon>
</el-button>
<span
class=
"time-label"
>
间隔
</span>
<el-input
v-model=
"timeInterval"
class=
"time-input"
></el-input>
<span
class=
"time-unit"
>
分钟
</span>
<el-button
@
click=
"reset"
>
重置
</el-button>
<div
class=
"time-desc"
>
<span
class=
"icon"
></span>
当前实时信号:
{{
currentData
.
compartNo
}}
仓 /
{{
currentData
.
row
}}
排
</div>
<div>
<el-button
@
click=
"navigateBackward"
>
<el-icon><ArrowLeftBold
/>
向前
</el-icon>
</el-button>
<el-button
@
click=
"navigateForward"
>
<el-icon><ArrowRightBold
/>
向后
</el-icon>
</el-button>
<span
class=
"time-label"
>
间隔
</span>
<el-input
v-model=
"timeInterval"
class=
"time-input"
></el-input>
<span
class=
"time-unit"
>
分钟
</span>
<el-button
@
click=
"reset"
>
重置
</el-button>
</div>
</div>
<table>
<thead>
<tr>
<th>
名称
</th>
<th
v-for=
"i in 20"
:key=
"i"
>
{{
i
}}
仓
</th>
<th
v-for=
"(value, index) in tableData"
:key=
"index"
>
{{
value
.
compartName
}}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
峰值_R
</td>
<td
v-for=
"(value, index) in peakValuesR"
:key=
"'r'+index"
>
{{
value
}}
</td>
<td>
峰值
{{
currentData
.
row
}}
排_R
</td>
<td
v-for=
"(value, index) in tableData"
:key=
"'r' + index"
:class=
"
{
'online-style': value.compartNo === currentData.compartNo,
}"
>
{{
value
.
peakValueR
}}
</td>
</tr>
<tr>
<td>
峰值_H
</td>
<td
v-for=
"(value, index) in peakValuesH"
:key=
"'h'+index"
>
{{
value
}}
</td>
<td
v-for=
"(value, index) in tableData"
:key=
"'h' + index"
>
{{
value
.
peakValueH
}}
</td>
</tr>
<tr>
<td>
状态
</td>
<td
v-for=
"(value, index) in statusValues"
:key=
"'s'+index"
:class=
"
{ 'error': value === '故障' }">
{{
value
}}
</td>
<td
v-for=
"(value, index) in tableData"
:key=
"'s' + index"
:class=
"
{ error: value.status === 1 }"
>
{{
value
.
status
===
1
?
"故障"
:
"正常"
}}
</td>
</tr>
<tr>
<td>
反吹中
</td>
<td
v-for=
"(value, index) in backwashValues"
:key=
"'b'+index"
>
{{
value
}}
</td>
<td
v-for=
"(value, index) in tableData"
:key=
"'b' + index"
>
{{
value
.
blowBack
}}
</td>
</tr>
<tr>
<td>
谷值
</td>
<td
v-for=
"(value, index) in valleyValues"
:key=
"'v'+index"
>
{{
value
}}
</td>
<td
v-for=
"(value, index) in tableData"
:key=
"'v' + index"
>
{{
value
.
valleyValue
}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</
template
>
<
script
setup
>
import
{
ref
,
reactive
,
onMounted
,
onBeforeUnmount
}
from
'vue'
import
*
as
echarts
from
'echarts'
import
{
ArrowRightBold
,
ArrowLeftBold
}
from
'@element-plus/icons-vue'
import
{
getBagMonitoringChartOption
}
from
'@/utils/chart.js'
import
{
ref
,
reactive
,
onMounted
,
onBeforeUnmount
,
watch
}
from
"vue"
;
import
*
as
echarts
from
"echarts"
;
import
moment
from
"moment"
;
import
{
ArrowRightBold
,
ArrowLeftBold
}
from
"@element-plus/icons-vue"
;
import
{
getBagMonitoringChartOption
}
from
"@/utils/chart.js"
;
import
{
getMonitorToday
,
getMonitorList
,
getMonitorRealtime
,
}
from
"@/request/api/bagMonitor"
;
import
{
useRoute
}
from
"vue-router"
;
// 获取路由参数
const
route
=
useRoute
();
// 设备选择
const
selectedDevice
=
ref
(
'1'
)
const
selectedDevice
=
ref
(
null
);
const
isPanelOpen
=
ref
(
true
)
const
isPanelOpen
=
ref
(
true
)
;
// 图表相关
const
chartRef
=
ref
(
null
)
let
chartInstance
=
null
let
updateTimer
=
null
// 定时器引用
// 时间导航相关
let
isRealTimeMode
=
true
let
lastTimePoint
=
null
const
chartRef
=
ref
(
null
);
let
chartInstance
=
null
;
let
updateTimer
=
null
;
// 定时器引用
let
lastTimePoint
=
null
;
// 控制是否获取实时设备状态数据
let
showRealtimeDeviceStatus
=
true
;
// 从路由参数获取dusterNo
const
dusterNo
=
ref
(
route
.
params
.
dusterNo
||
route
.
query
.
dusterNo
||
""
);
// 默认值作为fallback
const
dusterName
=
ref
(
route
.
params
.
dusterName
||
route
.
query
.
dusterName
||
""
);
// 图表数据
const
chartData
=
reactive
({
xData
:
[],
seriesData
:
[]
})
seriesData
:
[],
});
const
deviceList
=
ref
([]);
const
currentTime
=
ref
(
null
);
// 当前数据面板
const
currentData
=
reactive
({
time
:
'641'
,
chamber
:
'5'
,
backwash
:
'0'
,
di3
:
'0, 0,'
,
row
:
'1,'
,
realValue
:
'18.87,'
,
baseline
:
'10,'
,
delay
:
'0,'
,
totaltime
:
'1837'
,
next
:
'next:765,'
,
valve
:
'5'
,
di_time
:
'1848'
,
false
:
'0:0,'
,
peak
:
'4, 1, 46'
})
// 次要数据面板
const
secondaryData
=
reactive
({
time
:
'784'
,
chamber
:
'6'
,
backwash
:
'0'
,
valleyValue
:
'3'
,
status
:
'212'
,
falseVal
:
'0'
})
const
currentData
=
ref
({
realtime
:
""
,
time
:
""
,
compartNo
:
""
,
blowBack
:
""
,
di3
:
""
,
row
:
""
,
realData
:
""
,
baseline
:
""
,
delay
:
""
,
totalTime
:
""
,
next
:
""
,
valveNo
:
""
,
diTime
:
""
,
peak
:
""
,
});
// 表格数据
const
peakValuesR
=
[
0
,
0
,
-
30
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
const
peakValuesH
=
[
30
,
56
,
60
,
70
,
18
,
56
,
18
,
41
,
41
,
0
,
41
,
41
,
41
,
41
,
41
,
41
,
500
,
41
,
31
,
41
]
const
statusValues
=
Array
(
20
).
fill
(
'正常'
)
statusValues
[
2
]
=
'故障'
statusValues
[
16
]
=
'故障'
const
backwashValues
=
[
0
,
15
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
const
valleyValues
=
[
-
8
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
-
9
,
0
,
0
,
0
,
0
,
0
]
const
tableData
=
ref
([
{
compartName
:
"默认"
,
peakValueR
:
0
,
peakValueH
:
0
,
status
:
0
,
blowBack
:
0
,
valleyValue
:
0
,
},
]);
// 时间控制
const
timeInterval
=
ref
(
'10'
)
const
timeInterval
=
ref
(
"10"
);
const
togglePanel
=
()
=>
{
isPanelOpen
.
value
=
!
isPanelOpen
.
value
}
isPanelOpen
.
value
=
!
isPanelOpen
.
value
;
}
;
// 格式化时间为 HH:MM:SS 格式
const
formatTime
=
(
date
)
=>
{
const
hours
=
date
.
getHours
().
toString
().
padStart
(
2
,
'0'
)
const
minutes
=
date
.
getMinutes
().
toString
().
padStart
(
2
,
'0'
)
const
seconds
=
date
.
getSeconds
().
toString
().
padStart
(
2
,
'0'
)
return
`
${
hours
}
:
${
minutes
}
:
${
seconds
}
`
}
const
handleDeviceChange
=
(
value
)
=>
{
console
.
log
(
"value"
,
value
);
initChart
();
};
// 初始化图表
const
initChart
=
()
=>
{
if
(
chartInstance
)
{
chartInstance
.
dispose
()
chartInstance
.
dispose
()
;
}
// 初始化图表数据
initChartData
()
chartInstance
=
echarts
.
init
(
chartRef
.
value
)
// updateChart()
// 启动定时更新
startRealTimeUpdates
()
}
initChartData
();
};
// 初始化图表数据
const
initChartData
=
()
=>
{
const
now
=
new
Date
()
chartData
.
xData
=
[]
chartData
.
seriesData
=
[]
// 创建初始数据点,从5分钟前开始
for
(
let
i
=
300
;
i
>=
0
;
i
--
)
{
const
time
=
new
Date
(
now
.
getTime
()
-
i
*
1000
)
chartData
.
xData
.
push
(
formatTime
(
time
))
chartData
.
seriesData
.
push
(
generateRandomValue
())
if
(
!
dusterNo
.
value
||
!
selectedDevice
.
value
)
{
console
.
warn
(
"缺少dusterNo或deviceNo,无法获取数据"
);
return
;
}
}
// 生成一个介于 20-300 之间的随机值,模拟实时数据
const
generateRandomValue
=
()
=>
{
// 有10%的概率生成峰值
if
(
Math
.
random
()
<
0.1
)
{
return
Math
.
floor
(
Math
.
random
()
*
200
)
+
200
// 200-400之间的峰值
}
return
Math
.
floor
(
Math
.
random
()
*
130
)
+
20
// 20-150之间的基础值
}
// 获取昨天这个之前的数据
const
startTime
=
moment
()
.
subtract
(
5
,
"minutes"
)
.
format
(
"YYYY-MM-DD HH:mm:ss"
);
const
endTime
=
moment
().
format
(
"YYYY-MM-DD HH:mm:ss"
);
chartData
.
xData
=
[];
chartData
.
seriesData
=
[];
getMonitorToday
({
dusterNo
:
dusterNo
.
value
,
deviceNo
:
selectedDevice
.
value
,
startTime
:
startTime
,
// endTime: endTime,
})
.
then
((
res
)
=>
{
// 将data中的date和realData数据添加到chartData中,要求大数据量下速度快
if
(
res
.
data
&&
res
.
data
.
length
>
0
)
{
res
.
data
.
forEach
((
item
)
=>
{
chartData
.
xData
.
push
(
item
.
date
);
chartData
.
seriesData
.
push
(
item
.
realData
);
});
// 当前时间
currentTime
.
value
=
res
.
data
[
res
.
data
.
length
-
1
].
date
;
chartInstance
=
echarts
.
init
(
chartRef
.
value
);
// 启动定时更新
startRealTimeUpdates
();
}
else
{
console
.
warn
(
"未获取到初始数据"
);
}
})
.
catch
((
err
)
=>
{
console
.
error
(
"获取初始数据失败:"
,
err
);
});
};
// 更新图表
const
updateChart
=
()
=>
{
if
(
chartInstance
)
{
chartInstance
.
setOption
(
getBagMonitoringChartOption
(
chartData
.
xData
,
chartData
.
seriesData
))
chartInstance
.
setOption
(
getBagMonitoringChartOption
(
chartData
.
xData
,
chartData
.
seriesData
)
);
}
}
}
;
// 启动实时更新
const
startRealTimeUpdates
=
()
=>
{
if
(
updateTimer
)
{
clearInterval
(
updateTimer
)
clearInterval
(
updateTimer
)
;
}
updateTimer
=
setInterval
(()
=>
{
// 添加新数据点
const
now
=
new
Date
()
chartData
.
xData
.
push
(
formatTime
(
now
))
chartData
.
seriesData
.
push
(
generateRandomValue
())
// 移除最旧的数据点以保持固定长度
if
(
chartData
.
xData
.
length
>
31
)
{
// 保持30秒的数据
chartData
.
xData
.
shift
()
chartData
.
seriesData
.
shift
()
}
// 更新图表
updateChart
()
updateDataPanels
()
},
1000
)
isRealTimeMode
=
true
}
// 停止实时更新
const
stopRealTimeUpdates
=
()
=>
{
if
(
updateTimer
)
{
clearInterval
(
updateTimer
)
updateTimer
=
null
updateTimer
=
setInterval
(()
=>
{
updateFunction
();
},
1000
);
showRealtimeDeviceStatus
=
true
;
};
// 更新函数
const
updateFunction
=
()
=>
{
if
(
!
dusterNo
.
value
||
!
selectedDevice
.
value
)
return
;
// 从API获取实时数据
getMonitorRealtime
({
dusterNo
:
dusterNo
.
value
,
deviceNo
:
selectedDevice
.
value
,
})
.
then
((
res
)
=>
{
if
(
res
.
data
)
{
// 更新数据面板
if
(
res
.
data
.
realtimeCompart
)
{
tableData
.
value
=
res
.
data
.
realtimeCompart
;
}
if
(
res
.
data
.
realtimePanel
)
{
currentData
.
value
=
res
.
data
.
realtimePanel
;
}
}
})
.
catch
((
err
)
=>
{
console
.
error
(
"获取实时数据失败:"
,
err
);
});
if
(
showRealtimeDeviceStatus
)
{
// 结束时间往后推一秒
let
endTime
=
moment
(
new
Date
(
currentTime
.
value
))
.
add
(
1
,
"seconds"
)
.
format
(
"YYYY-MM-DD HH:mm:ss"
);
getMonitorToday
({
dusterNo
:
dusterNo
.
value
,
deviceNo
:
selectedDevice
.
value
,
startTime
:
currentTime
.
value
,
endTime
:
endTime
,
}).
then
((
res
)
=>
{
if
(
res
.
data
&&
res
.
data
.
length
>
0
)
{
const
item
=
res
.
data
[
res
.
data
.
length
-
1
];
chartData
.
xData
.
push
(
item
.
date
);
chartData
.
seriesData
.
push
(
item
.
realData
||
0
);
// 移除最旧的数据点以保持固定长度
if
(
chartData
.
xData
.
length
>
301
)
{
// 保持300秒的数据
chartData
.
xData
.
shift
();
chartData
.
seriesData
.
shift
();
}
currentTime
.
value
=
endTime
;
// 更新图表
updateChart
();
}
});
}
isRealTimeMode
=
false
}
};
// 向前导航(查看更早的数据)
const
navigateBackward
=
()
=>
{
// 关闭实时更新
stopRealTimeUpdates
()
// 不完全关闭轮询,只禁用实时设备状态获取
showRealtimeDeviceStatus
=
false
;
// 获取时间间隔(分钟)
const
interval
=
parseInt
(
timeInterval
.
value
)
||
10
const
intervalMs
=
interval
*
60
*
1000
const
interval
=
parseInt
(
timeInterval
.
value
)
||
10
;
const
intervalMs
=
interval
*
60
*
1000
;
// 如果是第一次点击,以当前最后一条数据的时间为参考
if
(
lastTimePoint
===
null
&&
chartData
.
xData
.
length
>
0
)
{
const
lastTimeString
=
chartData
.
xData
[
chartData
.
xData
.
length
-
1
]
const
[
hours
,
minutes
,
seconds
]
=
lastTimeString
.
split
(
':'
).
map
(
Number
)
const
now
=
new
Date
()
lastTimePoint
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
(),
now
.
getDate
(),
hours
,
minutes
,
seconds
)
const
lastTimeString
=
chartData
.
xData
[
chartData
.
xData
.
length
-
1
];
lastTimePoint
=
new
Date
(
lastTimeString
);
}
else
if
(
lastTimePoint
===
null
)
{
// 如果没有数据,使用当前时间
lastTimePoint
=
new
Date
()
lastTimePoint
=
new
Date
()
;
}
// 计算新的时间范围
const
endTime
=
new
Date
(
lastTimePoint
.
getTime
()
-
intervalMs
)
lastTimePoint
=
endTime
const
endTime
=
new
Date
(
lastTimePoint
.
getTime
()
-
intervalMs
);
lastTimePoint
=
endTime
;
// 重新生成数据
generateHistoricalData
(
endTime
,
interval
)
}
generateHistoricalData
(
endTime
,
interval
)
;
}
;
// 向后导航(查看更近的数据)
const
navigateForward
=
()
=>
{
//
关闭实时更新
s
topRealTimeUpdates
()
//
不完全关闭轮询,只禁用实时设备状态获取
s
howRealtimeDeviceStatus
=
false
;
// 获取时间间隔(分钟)
const
interval
=
parseInt
(
timeInterval
.
value
)
||
10
const
intervalMs
=
interval
*
60
*
1000
const
interval
=
parseInt
(
timeInterval
.
value
)
||
10
;
const
intervalMs
=
interval
*
60
*
1000
;
// 如果是第一次点击,以当前最后一条数据的时间为参考
if
(
lastTimePoint
===
null
&&
chartData
.
xData
.
length
>
0
)
{
const
lastTimeString
=
chartData
.
xData
[
chartData
.
xData
.
length
-
1
]
const
[
hours
,
minutes
,
seconds
]
=
lastTimeString
.
split
(
':'
).
map
(
Number
)
const
now
=
new
Date
()
lastTimePoint
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
(),
now
.
getDate
(),
hours
,
minutes
,
seconds
)
const
lastTimeString
=
chartData
.
xData
[
chartData
.
xData
.
length
-
1
];
lastTimePoint
=
new
Date
(
lastTimeString
);
}
else
if
(
lastTimePoint
===
null
)
{
// 如果没有数据,使用当前时间
lastTimePoint
=
new
Date
()
lastTimePoint
=
new
Date
()
;
}
// 计算新的时间范围
const
endTime
=
new
Date
(
lastTimePoint
.
getTime
()
+
intervalMs
)
let
endTime
=
new
Date
(
lastTimePoint
.
getTime
()
+
intervalMs
);
// // 如果新的结束时间超过当前时间,则切换回实时模式
const
now
=
new
Date
()
const
now
=
moment
();
if
(
endTime
>
now
)
{
lastTimePoint
=
null
// startRealTimeUpdates()
return
lastTimePoint
=
new
Date
(
now
);
endTime
=
new
Date
(
now
);
// // showRealtimeDeviceStatus = true;
// return;
}
else
{
lastTimePoint
=
endTime
;
}
lastTimePoint
=
endTime
// 重新生成数据
generateHistoricalData
(
endTime
,
interval
)
}
generateHistoricalData
(
endTime
,
interval
)
;
}
;
const
reset
=
()
=>
{
// 清空现有数据
chartData
.
xData
=
[]
chartData
.
seriesData
=
[]
lastTimePoint
=
null
chartData
.
xData
=
[];
chartData
.
seriesData
=
[];
lastTimePoint
=
null
;
// 恢复实时设备状态获取
showRealtimeDeviceStatus
=
true
;
// 重新生成当前时间往前300秒的数据
const
now
=
new
Date
()
generateHistoricalData
(
now
,
5
)
const
now
=
new
Date
();
generateHistoricalData
(
now
,
5
);
// 开启实时更新
startRealTimeUpdate
()
}
startRealTimeUpdate
()
;
}
;
// 开启实时更新(重置用)
const
startRealTimeUpdate
=
()
=>
{
// 确保之前的定时器已清除
stopRealTimeUpdates
()
// 初始化实时模式标志
isRealTimeMode
=
true
if
(
updateTimer
)
{
clearInterval
(
updateTimer
);
updateTimer
=
null
;
}
// 启动定时更新
updateTimer
=
setInterval
(()
=>
{
// 添加新数据点
const
now
=
new
Date
()
chartData
.
xData
.
push
(
formatTime
(
now
))
chartData
.
seriesData
.
push
(
generateRandomValue
())
// 移除最旧的数据点以保持固定长度
if
(
chartData
.
xData
.
length
>
31
)
{
// 保持30秒的数据
chartData
.
xData
.
shift
()
chartData
.
seriesData
.
shift
()
}
// 更新图表
updateChart
()
updateDataPanels
()
},
1000
)
}
updateFunction
();
},
1000
);
};
// 生成历史数据
const
generateHistoricalData
=
(
endTime
,
intervalMinutes
)
=>
{
// 清空现有数据
chartData
.
xData
=
[]
chartData
.
seriesData
=
[]
// 计算开始时间(结束时间减去间隔)
const
startTime
=
new
Date
(
endTime
.
getTime
()
-
intervalMinutes
*
60
*
1000
)
// 生成数据点(每秒一个)
for
(
let
time
=
new
Date
(
startTime
);
time
<=
endTime
;
time
=
new
Date
(
time
.
getTime
()
+
1000
))
{
chartData
.
xData
.
push
(
formatTime
(
time
))
chartData
.
seriesData
.
push
(
generateRandomValue
())
}
// 更新图表
updateChart
()
}
if
(
!
dusterNo
.
value
||
!
selectedDevice
.
value
)
return
;
// 更新数据面板
const
updateDataPanels
=
()
=>
{
// 随机更新当前数据面板的实时值
currentData
.
realValue
=
(
Math
.
random
()
*
30
+
10
).
toFixed
(
2
)
+
','
// 更新时间显示
const
now
=
new
Date
()
currentData
.
time
=
Math
.
floor
((
now
.
getTime
()
/
1000
)
%
1000
).
toString
()
// 每10秒更新一次次要数据面板
if
(
now
.
getSeconds
()
%
1
===
0
)
{
secondaryData
.
time
=
Math
.
floor
((
now
.
getTime
()
/
1000
)
%
1000
).
toString
()
secondaryData
.
valleyValue
=
Math
.
floor
(
Math
.
random
()
*
10
).
toString
()
}
}
// 清空现有数据
chartData
.
xData
=
[];
chartData
.
seriesData
=
[];
const
startTime
=
new
Date
(
endTime
.
getTime
()
-
intervalMinutes
*
60
*
1000
);
const
startTimeStr
=
moment
(
startTime
).
format
(
"YYYY-MM-DD HH:mm:ss"
);
const
endTimeStr
=
moment
(
endTime
).
format
(
"YYYY-MM-DD HH:mm:ss"
);
// 从API获取历史数据
getMonitorToday
({
dusterNo
:
dusterNo
.
value
,
deviceNo
:
selectedDevice
.
value
,
startTime
:
startTimeStr
,
endTime
:
endTimeStr
,
})
.
then
((
res
)
=>
{
if
(
res
.
data
&&
res
.
data
.
length
>
0
)
{
res
.
data
.
forEach
((
item
)
=>
{
chartData
.
xData
.
push
(
item
.
date
);
chartData
.
seriesData
.
push
(
item
.
realData
);
});
// 更新图表
updateChart
();
}
})
.
catch
((
err
)
=>
{
console
.
error
(
"获取历史数据失败:"
,
err
);
});
};
// 监听窗口大小变化,调整图表大小
const
handleResize
=
()
=>
{
if
(
chartInstance
)
{
chartInstance
.
resize
()
chartInstance
.
resize
()
;
}
}
};
const
getDeviceList
=
()
=>
{
if
(
!
dusterNo
.
value
)
return
;
getMonitorList
({
dusterNo
:
dusterNo
.
value
,
}).
then
((
res
)
=>
{
if
(
res
.
data
)
{
deviceList
.
value
=
res
.
data
.
devices
||
[];
if
(
deviceList
.
value
.
length
>
0
)
{
selectedDevice
.
value
=
deviceList
.
value
[
0
].
deviceNo
;
}
tableData
.
value
=
Array
.
from
(
{
length
:
res
.
data
.
compartCount
},
(
item
,
index
)
=>
({
compartName
:
`
${
index
+
1
}
仓`
,
peakValueR
:
0
,
peakValueH
:
0
,
status
:
0
,
blowBack
:
0
,
})
);
initChart
();
}
});
};
// 监听dusterNo变化
watch
(
dusterNo
,
(
newVal
)
=>
{
if
(
newVal
)
{
// 重新获取设备列表
getDeviceList
();
}
});
onMounted
(()
=>
{
initChart
()
window
.
addEventListener
(
'resize'
,
handleResize
)
})
if
(
dusterNo
.
value
)
{
getDeviceList
();
}
window
.
addEventListener
(
"resize"
,
handleResize
);
});
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
'resize'
,
handleResize
)
window
.
removeEventListener
(
"resize"
,
handleResize
);
// 清除定时器
if
(
updateTimer
)
{
clearInterval
(
updateTimer
)
updateTimer
=
null
clearInterval
(
updateTimer
)
;
updateTimer
=
null
;
}
// 销毁图表实例
if
(
chartInstance
)
{
chartInstance
.
dispose
()
chartInstance
=
null
chartInstance
.
dispose
()
;
chartInstance
=
null
;
}
})
})
;
</
script
>
<
style
scoped
lang=
"scss"
>
...
...
@@ -435,15 +555,16 @@ onBeforeUnmount(() => {
width
:
100%
;
height
:
calc
(
100%
-
14px
);
box-sizing
:
border-box
;
overflow-x
:
hidden
;
// overflow-x: hidden;
position
:
relative
;
.header
{
position
:
relative
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
margin-bottom
:
1
0
px
;
margin-bottom
:
1
6
px
;
.select-container
{
position
:
absolute
;
left
:
0
;
...
...
@@ -452,28 +573,28 @@ onBeforeUnmount(() => {
display
:
flex
;
align-items
:
center
;
z-index
:
99
;
span
{
display
:
block
;
width
:
110px
;
margin-right
:
10px
;
}
.device-number
{
background
:
#fcf0c2
;
padding
:
2px
8px
;
margin-left
:
10px
;
border-radius
:
4
px
;
border-radius
:
6
px
;
}
}
.title
{
font-size
:
18px
;
font-weight
:
bold
;
color
:
#
409EFF
;
color
:
#
2182a0
;
}
}
.chart-container
{
width
:
100%
;
height
:
calc
(
100%
-
350px
);
...
...
@@ -481,106 +602,127 @@ onBeforeUnmount(() => {
border-radius
:
4px
;
display
:
flex
;
flex-direction
:
column
;
position
:
relative
;
.chart-wrapper
{
flex
:
1
;
width
:
100%
;
}
.data-panel
{
position
:
absolute
;
top
:
80px
;
right
:
25px
;
width
:
320px
;
z-index
:
10
;
transition
:
transform
0
.3s
ease-in-out
;
&
.collapsed
{
transform
:
translateX
(
345px
);
}
.panel-toggle
{
position
:
absolute
;
width
:
25px
;
height
:
30px
;
background
:
#fff
;
top
:
1px
;
.no-data-text
{
width
:
100%
;
height
:
100%
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
font-size
:
14px
;
color
:
#999
;
background-color
:
#f5f7fa
;
border-radius
:
4px
;
}
}
.no-data
{
position
:
absolute
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
100%
;
z-index
:
10
;
}
}
.collapsed-icon
{
left
:
-23px
;
}
.expanded-icon
{
right
:
-23px
;
}
.data-box
{
padding
:
10px
;
background-color
:
rgba
(
255
,
255
,
255
,
0
.9
);
border-radius
:
4px
;
border
:
1px
solid
#ebeef5
;
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0
.1
);
margin-bottom
:
10px
;
.data-item
{
display
:
flex
;
justify-content
:
space-between
;
margin-bottom
:
5px
;
font-size
:
14px
;
div
{
flex
:
1
;
}
.data-panel
{
position
:
absolute
;
top
:
0px
;
right
:
0px
;
width
:
320px
;
z-index
:
100
;
transition
:
transform
0
.3s
ease-in-out
;
&
.collapsed
{
transform
:
translateX
(
345px
);
}
.panel-toggle
{
position
:
absolute
;
width
:
25px
;
height
:
30px
;
background
:
#fff
;
top
:
1px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
border-radius
:
4px
;
}
.collapsed-icon
{
left
:
-23px
;
}
.expanded-icon
{
right
:
-23px
;
}
.data-box
{
padding
:
10px
;
background-color
:
rgba
(
255
,
255
,
255
,
0
.9
);
border-radius
:
4px
;
border
:
1px
solid
#ebeef5
;
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0
.1
);
margin-bottom
:
10px
;
.data-item
{
display
:
flex
;
justify-content
:
space-between
;
margin-bottom
:
5px
;
font-size
:
14px
;
div
{
flex
:
1
;
}
}
.second-box
{
font-size
:
12px
;
}
}
.second-box
{
font-size
:
12px
;
}
}
.data-table
{
background-color
:
#fff
;
border-radius
:
4px
;
padding
:
10px
0
;
box-sizing
:
border-box
;
table
{
width
:
100%
;
border-collapse
:
collapse
;
th
,
td
{
border
:
1px
solid
#EBEEF5
;
th
,
td
{
border
:
1px
solid
#ebeef5
;
text-align
:
center
;
padding
:
8px
;
font-size
:
14px
;
}
th
{
background-color
:
#
F5F7FA
;
background-color
:
#
f5f7fa
;
color
:
#606266
;
}
.error
{
color
:
#
F56C6C
;
color
:
#
f56c6c
;
}
.highlight
{
background-color
:
#
FCF8E
3
;
background-color
:
#
fcf8e
3
;
position
:
relative
;
&
:
:
after
{
content
:
""
;
position
:
absolute
;
width
:
16px
;
height
:
16px
;
background-color
:
#
E6A23C
;
background-color
:
#
e6a23c
;
border-radius
:
50%
;
top
:
50%
;
left
:
50%
;
...
...
@@ -588,18 +730,49 @@ onBeforeUnmount(() => {
z-index
:
1
;
}
}
.online-style
{
background-color
:
rgba
(
33
,
130
,
160
,
1
);
color
:
#fff
;
}
}
.time-controls
{
margin-bottom
:
10px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
flex-end
;
justify-content
:
space-between
;
.time-label
,
.time-unit
{
.el-button
{
background-color
:
#2182a0
;
border-color
:
#2182a0
;
color
:
#fff
;
&
:hover
{
background-color
:
#1a6980
;
border-color
:
#1a6980
;
}
}
.time-desc
{
font-size
:
14px
;
color
:
#606266
;
display
:
flex
;
align-items
:
center
;
.icon
{
display
:
inline-block
;
width
:
12px
;
height
:
12px
;
background-color
:
#05ea09
;
border-radius
:
50%
;
margin-right
:
5px
;
}
}
.time-label
,
.time-unit
{
font-size
:
14px
;
margin
:
0
5px
;
margin-right
:
10px
;
}
.time-input
{
width
:
60px
;
}
...
...
@@ -607,4 +780,3 @@ onBeforeUnmount(() => {
}
}
</
style
>
\ No newline at end of file
src/views/dustMonitoring/components/healthyProgress.vue
View file @
ebcf087e
...
...
@@ -9,7 +9,7 @@
class=
"healthy-progress-bar h-full transition-all duration-500 ease-out bg-gradient-to-r from-secondary to-green-400 diagonal-pattern diagonal-pattern-animation"
></div>
<div
class=
"justify-between items-center mb-2 healthy-text-position"
>
<span
class=
"font-semibold"
>
健康度
</span>
<span
class=
"font-semibold"
>
健康度
:
</span>
<span
id=
"square-progress-value"
class=
"text-lg font-bold"
>
45%
</span>
</div>
</div>
...
...
src/views/dustMonitoring/components/warn.vue
View file @
ebcf087e
<
template
>
<div
class=
"title layout1"
>
<span
class=
"warn-title"
>
{{
title
}}
:
</span>
<
span
class=
"jump-icon"
>
>>
</span
>
<span
class=
"warn-title"
>
告警
:
</span>
<
!--
<span
class=
"jump-icon"
>
>>
</span>
--
>
</div>
<div
class=
"content"
>
<div
class=
"item"
v-for=
"item in listInfo"
:key=
"item"
>
...
...
@@ -45,12 +45,15 @@ const props = defineProps({
width
:
100%
;
display
:
flex
;
align-items
:
center
;
margin-bottom
:
10
px
;
margin-bottom
:
5
px
;
padding
:
4px
0px
4px
2px
;
box-shadow
:
rgba
(
53
,
58
,
62
,
0
.08
)
0px
1px
10px
0px
,
rgba
(
53
,
58
,
62
,
0
.08
)
0px
2px
4px
0px
;
box-shadow
:
rgba
(
53
,
58
,
62
,
0
.04
)
0px
1px
10px
0px
,
rgba
(
53
,
58
,
62
,
0
.04
)
0px
2px
4px
0px
;
font-size
:
14px
;
color
:
#333
;
img
{
width
:
20px
;
height
:
auto
;
margin-right
:
10px
;
}
}
}
...
...
src/views/dustMonitoring/index.vue
View file @
ebcf087e
...
...
@@ -83,6 +83,7 @@
}"
v-for="item in detailObj.compartHealthList[0]"
:key="item"
@click="handleStatusDotClick()"
>
</div>
</div>
<div
class=
"part"
>
...
...
@@ -131,6 +132,7 @@
</
template
>
<
script
setup
>
import
{
ref
,
reactive
,
onMounted
,
onUnmounted
,
watch
,
computed
}
from
"vue"
;
import
{
useRouter
,
useRoute
}
from
"vue-router"
;
import
*
as
echarts
from
"echarts"
;
import
{
getDataFun
}
from
"@/request/method.js"
;
import
{
useUsersStore
}
from
"@/pinia/user.js"
;
...
...
@@ -138,6 +140,7 @@ import moment from "moment";
import
warnCom
from
"./components/warn.vue"
;
import
healthyCom
from
"./components/healthyProgress.vue"
;
import
{
getLineOption2
,
getGaugeOption
}
from
"@/utils/chart"
;
const
userStore
=
useUsersStore
();
const
form
=
reactive
({
dusterNo
:
""
,
...
...
@@ -179,6 +182,8 @@ const option = {
},
],
};
const
router
=
useRouter
();
const
route
=
useRoute
();
const
chartInstance
=
reactive
({});
const
chartData
=
reactive
([{},
{},
{}]);
const
initChart
=
(
instance
,
option
,
domEl
)
=>
{
...
...
@@ -304,7 +309,7 @@ const getDusterNameEnum = () => {
.
then
((
res
)
=>
{
dusterList
.
value
=
(
res
&&
res
.
data
)
||
[];
// 判断除尘器名称是否有值
form
.
dusterNo
=
dusterList
.
value
[
0
].
dusterNo
;
form
.
dusterNo
=
route
.
query
.
dusterNo
||
dusterList
.
value
[
0
].
dusterNo
;
})
.
catch
(()
=>
{
dusterList
.
value
=
[];
...
...
@@ -583,6 +588,16 @@ const dusterName = computed(() => {
return
dusterList
.
value
.
find
((
item
)
=>
item
.
dusterNo
==
form
.
dusterNo
)
?.
dusterName
;
});
const
handleStatusDotClick
=
()
=>
{
router
.
push
({
path
:
"/bag-monitor"
,
query
:
{
dusterNo
:
form
.
dusterNo
,
dusterName
:
dusterName
.
value
,
}
});
};
</
script
>
<
style
lang=
"scss"
scoped
>
$borderColor
:
#bbbdc3
91
;
...
...
@@ -627,7 +642,7 @@ $borderColor: #bbbdc391;
.part1
{
width
:
100%
;
height
:
6
vh
;
height
:
5
vh
;
border-radius
:
6px
;
border
:
1px
solid
rgba
(
13
,
15
,
18
,
0
.1
);
box-shadow
:
0px
3px
6px
0px
rgba
(
13
,
15
,
18
,
0
.1
);
...
...
@@ -642,7 +657,7 @@ $borderColor: #bbbdc391;
.part2
{
margin-top
:
10px
;
width
:
100%
;
height
:
calc
(
5
7
vh
-
10px
);
height
:
calc
(
5
8
vh
-
10px
);
border-radius
:
6px
;
border
:
1px
solid
rgba
(
13
,
15
,
18
,
0
.1
);
box-shadow
:
0px
3px
6px
0px
rgba
(
13
,
15
,
18
,
0
.1
);
...
...
src/views/dustOverview/components/addDustCollectorDialog.vue
View file @
ebcf087e
<
template
>
<el-dialog
:model-value=
"modelValue"
:title=
"editData ? '编辑除尘器' : '新增除尘器'"
width=
"
46
0px"
:close-on-click-modal=
"false"
:close-on-press-escape=
"false"
@
update:model-value=
"
$emit('update:modelValue', $event)
"
>
<el-dialog
:model-value=
"modelValue"
:title=
"editData ? '编辑除尘器' : '新增除尘器'"
width=
"
50
0px"
:close-on-click-modal=
"false"
:close-on-press-escape=
"false"
@
update:model-value=
"
cancel
"
>
<div
class=
"add-dust-form"
>
<div
class=
"form-item"
>
<div
class=
"selector-wrap"
>
...
...
@@ -14,7 +14,7 @@
</div>
<div
class=
"form-content"
>
<el-form
ref=
"formRef"
:model=
"formData"
label-width=
"1
0
0px"
:rules=
"rules"
>
<el-form
ref=
"formRef"
:model=
"formData"
label-width=
"1
2
0px"
:rules=
"rules"
>
<el-form-item
label=
"除尘器名称"
prop=
"name"
>
<el-input
v-model=
"formData.name"
placeholder=
"请输入"
/>
</el-form-item>
...
...
@@ -38,7 +38,7 @@
<el-input
v-model
.
number=
"formData.valveCount"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"合理压差范围"
>
<el-form-item
label=
"合理压差范围"
required
>
<div
class=
"range-input"
>
<el-form-item
prop=
"pressureMin"
class=
"coordinate-item"
>
<el-input
v-model
.
number=
"formData.pressureMin"
placeholder=
"请输入"
/>
...
...
@@ -119,26 +119,12 @@ const formData = reactive({
coordinateY
:
null
});
// 监听editData变化,初始化表单数据
watch
(()
=>
props
.
editData
,
(
newVal
)
=>
{
if
(
newVal
)
{
formData
.
name
=
newVal
.
deviceName
;
formData
.
code
=
newVal
.
deviceNo
;
formData
.
process
=
newVal
.
productionLineId
;
formData
.
chamberCount
=
Number
(
newVal
.
compartNum
);
formData
.
valveCount
=
Number
(
newVal
.
valveNum
);
formData
.
pressureMin
=
Number
(
newVal
.
pressureDiffLow
);
formData
.
pressureMax
=
Number
(
newVal
.
pressureDiffHigh
);
formData
.
serverIp
=
newVal
.
serviceIp
;
formData
.
coordinateX
=
Number
(
newVal
.
x
);
formData
.
coordinateY
=
Number
(
newVal
.
y
);
}
},
{
immediate
:
true
});
const
rules
=
{
name
:
[{
required
:
true
,
message
:
'请输入除尘器名称'
,
trigger
:
'blur'
}],
code
:
[{
required
:
true
,
message
:
'请输入除尘器编号'
,
trigger
:
'blur'
}],
process
:
[{
required
:
true
,
message
:
'请选择所属工序'
,
trigger
:
'
change
'
}],
process
:
[{
required
:
true
,
message
:
'请选择所属工序'
,
trigger
:
'
blur
'
}],
chamberCount
:
[
{
required
:
true
,
message
:
'请输入仓室数量'
,
trigger
:
'blur'
},
{
type
:
'number'
,
min
:
1
,
message
:
'仓室数量必须大于0'
,
trigger
:
'blur'
}
...
...
@@ -149,7 +135,7 @@ const rules = {
],
pressureMin
:
[
{
required
:
true
,
message
:
'请输入最小压差'
,
trigger
:
'blur'
},
{
type
:
'number'
,
min
:
1
,
message
:
'最小压差必须大
于0'
,
trigger
:
'blur'
},
{
type
:
'number'
,
min
:
0
,
message
:
'最小压差不能小
于0'
,
trigger
:
'blur'
},
{
validator
:
(
rule
,
value
,
callback
)
=>
{
if
(
value
&&
formData
.
pressureMax
&&
value
>=
formData
.
pressureMax
)
{
...
...
@@ -163,7 +149,7 @@ const rules = {
],
pressureMax
:
[
{
required
:
true
,
message
:
'请输入最大压差'
,
trigger
:
'blur'
},
{
type
:
'number'
,
min
:
0
,
message
:
'最大压差
必须大
于0'
,
trigger
:
'blur'
},
{
type
:
'number'
,
min
:
0
,
message
:
'最大压差
不能小
于0'
,
trigger
:
'blur'
},
{
validator
:
(
rule
,
value
,
callback
)
=>
{
if
(
value
&&
formData
.
pressureMin
&&
value
<=
formData
.
pressureMin
)
{
...
...
@@ -236,7 +222,6 @@ const productionLine = () => {
onMounted
(()
=>
{
productionLine
();
initDustOptions
();
});
const
selectDustType
=
()
=>
{
...
...
@@ -270,7 +255,6 @@ const submitForm = () => {
if
(
props
.
editData
)
{
submitData
.
id
=
props
.
editData
.
id
;
}
// 发送数据到父组件处理保存逻辑
emit
(
'save'
,
submitData
);
// emit('update:modelValue', false);
...
...
@@ -279,10 +263,8 @@ const submitForm = () => {
}
});
};
const
cancel
=
()
=>
{
const
resetForm
=
()
=>
{
// 重置表单状态和数据
formRef
.
value
.
resetFields
();
formData
.
dustType
=
''
;
formData
.
name
=
''
;
formData
.
code
=
''
;
...
...
@@ -294,9 +276,38 @@ const cancel = () => {
formData
.
serverIp
=
''
;
formData
.
coordinateX
=
null
;
formData
.
coordinateY
=
null
;
emit
(
'update:modelValue'
,
false
);
}
const
cancel
=
()
=>
{
resetForm
();
formRef
.
value
.
resetFields
();
emit
(
'cancel'
,
{});
};
// 监听editData变化,初始化表单数据
watch
(()
=>
props
.
editData
,
(
newVal
)
=>
{
if
(
newVal
)
{
formData
.
name
=
newVal
.
deviceName
;
formData
.
code
=
newVal
.
deviceNo
;
formData
.
process
=
newVal
.
productionLineId
;
formData
.
chamberCount
=
Number
(
newVal
.
compartNum
);
formData
.
valveCount
=
Number
(
newVal
.
valveNum
);
formData
.
pressureMin
=
Number
(
newVal
.
pressureDiffLow
);
formData
.
pressureMax
=
Number
(
newVal
.
pressureDiffHigh
);
formData
.
serverIp
=
newVal
.
serviceIp
;
formData
.
coordinateX
=
Number
(
newVal
.
x
);
formData
.
coordinateY
=
Number
(
newVal
.
y
);
}
else
{
resetForm
();
}
},
{
immediate
:
true
});
watch
(()
=>
props
.
modelValue
,
(
newVal
)
=>
{
if
(
newVal
===
false
)
{
resetForm
();
}
},
{
immediate
:
true
});
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -343,7 +354,7 @@ const cancel = () => {
}
.form-footer
{
margin-top
:
2
0px
;
margin-top
:
3
0px
;
display
:
flex
;
justify-content
:
center
;
...
...
src/views/dustOverview/components/roomSettingDialog.vue
View file @
ebcf087e
...
...
@@ -48,6 +48,7 @@
style=
"width: 100%"
size=
"small"
border
:resizable=
"false"
>
<el-table-column
prop=
"compartPositionRow"
label=
"排"
width=
"180"
>
<template
#
default=
"
{ row }">
...
...
src/views/dustOverview/components/valveSettingDialog.vue
View file @
ebcf087e
...
...
@@ -5,7 +5,7 @@
width=
"800px"
:close-on-click-modal=
"false"
:close-on-press-escape=
"false"
@
update:model-value=
"
$emit('update:modelValue', $event)
"
@
update:model-value=
"
cancel
"
>
<div
class=
"valve-setting"
>
<div
class=
"setting-row"
>
...
...
@@ -37,6 +37,7 @@
:min=
"0"
controls-position=
"right"
style=
"width: 120px"
@
change=
"handleValveNumChange"
:class=
"
{ 'is-error': errorMessage }"
/>
</div>
...
...
@@ -47,6 +48,7 @@
:min=
"0"
controls-position=
"right"
style=
"width: 120px"
@
change=
"handleBagNumChange"
/>
</div>
</div>
...
...
@@ -57,14 +59,14 @@
{{
errorMessage
}}
</div>
<div>
<
!--
<
div>
<el-button
type=
"primary"
class=
"save-btn"
@
click=
"handleSave"
:disabled=
"!!errorMessage || valveForm.valveNum === null || valveForm.bagNum === null"
>
设置
</el-button>
</div>
</div>
-->
</div>
<!-- 分布表格 -->
...
...
@@ -93,7 +95,7 @@
<template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"
$emit('update:modelValue', false)
"
>
取消
</el-button>
<el-button
@
click=
"
cancel
"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"handleConfirm"
:disabled=
"!!errorMessage"
>
保存
</el-button>
...
...
@@ -119,7 +121,7 @@ const emit = defineEmits(["update:modelValue", "confirm"]);
const
valveForm
=
ref
({
total
:
0
,
rows
:
0
,
frontCompartNo
:
0
,
frontCompartNo
:
null
,
valveNum
:
0
,
bagNum
:
0
});
...
...
@@ -174,14 +176,25 @@ const initializeState = () => {
}
};
const
resetForm
=
()
=>
{
statusData
.
value
=
[];
valveForm
.
value
.
frontCompartNo
=
null
;
valveForm
.
value
.
valveNum
=
0
;
valveForm
.
value
.
bagNum
=
0
;
}
const
getAllocation
=
(
params
)
=>
{
getValveAllocation
(
params
).
then
(
res
=>
{
if
(
res
.
code
===
1
&&
res
.
data
.
valveList
.
length
>
0
)
{
statusData
.
value
=
res
.
data
.
valveList
;
valveForm
.
value
.
frontCompartNo
=
statusData
.
value
[
0
].
frontCompartNo
;
valveForm
.
value
.
valveNum
=
statusData
.
value
[
0
].
valveNum
;
valveForm
.
value
.
bagNum
=
statusData
.
value
[
0
].
bagNum
;
}
if
(
res
.
code
===
1
)
{
if
(
res
.
data
.
valveList
.
length
>
0
)
{
statusData
.
value
=
res
.
data
.
valveList
;
valveForm
.
value
.
frontCompartNo
=
statusData
.
value
[
0
].
frontCompartNo
;
valveForm
.
value
.
valveNum
=
statusData
.
value
[
0
].
valveNum
;
valveForm
.
value
.
bagNum
=
statusData
.
value
[
0
].
bagNum
;
}
else
{
resetForm
();
}
}
})
}
...
...
@@ -197,6 +210,45 @@ const handleRoomChange = (roomIndex) => {
}
};
// 监听电磁阀数量变化
const
handleValveNumChange
=
()
=>
{
// 验证是否超出限制
const
validation
=
validateValveCount
.
value
;
if
(
!
validation
.
valid
)
{
errorMessage
.
value
=
validation
.
message
;
return
;
}
// 保存当前选中仓室的电磁阀分布数量
const
selectedRoom
=
statusData
.
value
.
find
(
cell
=>
cell
.
frontCompartNo
===
valveForm
.
value
.
frontCompartNo
);
if
(
selectedRoom
)
{
selectedRoom
.
valveNum
=
valveForm
.
value
.
valveNum
;
selectedRoom
.
bagNum
=
valveForm
.
value
.
bagNum
;
// 更新错误信息
errorMessage
.
value
=
""
;
}
}
// 监听布袋数量变化
const
handleBagNumChange
=
()
=>
{
// 验证是否超出限制
const
validation
=
validateValveCount
.
value
;
if
(
!
validation
.
valid
)
{
errorMessage
.
value
=
validation
.
message
;
return
;
}
// 保存当前选中仓室的电磁阀分布数量
const
selectedRoom
=
statusData
.
value
.
find
(
cell
=>
cell
.
frontCompartNo
===
valveForm
.
value
.
frontCompartNo
);
if
(
selectedRoom
)
{
selectedRoom
.
valveNum
=
valveForm
.
value
.
valveNum
;
selectedRoom
.
bagNum
=
valveForm
.
value
.
bagNum
;
// 更新错误信息
errorMessage
.
value
=
""
;
}
}
watch
(()
=>
props
.
modelValue
,
(
newVal
)
=>
{
if
(
newVal
)
{
initializeState
();
...
...
@@ -251,7 +303,6 @@ const handleSave = () => {
const
handleConfirm
=
()
=>
{
// 最终确认时再次验证总数是否符合要求
console
.
log
(
statusData
.
value
);
const
totalValves
=
statusData
.
value
.
reduce
((
sum
,
cell
)
=>
sum
+
cell
.
valveNum
,
0
);
if
(
totalValves
>
valveForm
.
value
.
total
)
{
errorMessage
.
value
=
`电磁阀总数超出限制
${
totalValves
-
valveForm
.
value
.
total
}
个,请调整后再提交`
;
...
...
@@ -261,6 +312,11 @@ const handleConfirm = () => {
emit
(
"confirm"
,
statusData
.
value
);
// emit("update:modelValue", false);
};
const
cancel
=
()
=>
{
resetForm
();
emit
(
"update:modelValue"
,
false
);
}
</
script
>
<
style
lang=
"scss"
scoped
>
...
...
@@ -334,6 +390,7 @@ const handleConfirm = () => {
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
min-height
:
152px
;
padding
:
12px
;
background
:
#f5f7fa
;
border-radius
:
4px
;
...
...
@@ -345,7 +402,7 @@ const handleConfirm = () => {
}
.valve-cell
{
width
:
41px
;
min-
width
:
41px
;
background
:
white
;
padding
:
4px
8px
;
border-radius
:
4px
;
...
...
@@ -368,7 +425,7 @@ const handleConfirm = () => {
.cell-value
{
display
:
flex
;
justify-content
:
center
;
align-items
:
end
;
align-items
:
baseline
;
font-size
:
16px
;
color
:
#606266
;
span
{
...
...
src/views/dustOverview/index.vue
View file @
ebcf087e
<
template
>
<div
class=
"page-container dust-container"
>
<div
class=
"header"
>
<div
class=
"item-box"
>
<div
class=
"item-box
crusor-click"
@
click=
"handleDusterLeakNumClick
"
>
<img
src=
"@/assets/icons/warn.png"
alt=
"dust"
/>
<div
class=
"title"
>
<span>
泄漏告警(条)
</span>
...
...
@@ -17,7 +17,7 @@
</div>
</div>
<div
class=
"item-box"
>
<div
class=
"item-box
crusor-click"
@
click=
"handleCloseLoopNumClick
"
>
<img
src=
"@/assets/icons/close.png"
alt=
"dust"
/>
<div
class=
"title"
>
<span>
闭环(条)
</span>
...
...
@@ -102,18 +102,19 @@
<
template
#
compartHealthList=
"{ row }"
>
<div
class=
"status-matrix"
v-if=
"row.compartHealthList.length > 0"
>
<div
v-for=
"(
r
ow, rowIndex) in row.compartHealthList"
v-for=
"(
childR
ow, rowIndex) in row.compartHealthList"
:key=
"rowIndex"
class=
"matrix-row"
>
<div
v-for=
"(status, colIndex) in
r
ow"
v-for=
"(status, colIndex) in
childR
ow"
:key=
"colIndex"
class=
"status-dot"
@
click=
"handleStatusDotClick(row, colIndex)"
:class=
"
{
'status-normal': status.healthStatus === 1,
'status-warning': status.healthStatus ===
2
,
'status-error': status.healthStatus ===
3
,
'status-warning': status.healthStatus ===
3
,
'status-error': status.healthStatus ===
2
,
}"
>
</div>
</div>
...
...
@@ -158,6 +159,7 @@
v-model=
"isAddDustCollectorVisible"
:edit-data=
"currentEditingDustCollector"
@
save=
"handleSaveDustCollector"
@
cancel=
"handleCancel"
/>
</div>
</template>
...
...
@@ -175,12 +177,15 @@ import {
postAddCompartAllocation
,
postAddValveInfo
,
}
from
"@/request/api/dustOverview"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
getToken
}
from
"@/utils/auth"
;
import
CommonTable
from
"@/components/commonTable/index.vue"
;
import
{
ElMessageBox
,
ElMessage
}
from
"element-plus"
;
import
RoomSettingDialog
from
"./components/roomSettingDialog.vue"
;
import
ValveSettingDialog
from
"./components/valveSettingDialog.vue"
;
import
AddDustCollectorDialog
from
"./components/addDustCollectorDialog.vue"
;
import
moment
from
"moment"
;
const
router
=
useRouter
();
const
formInline
=
ref
({
deviceName
:
""
,
...
...
@@ -280,19 +285,10 @@ const filterProductionLine = (query) => {
}
};
const
getStatusType
=
(
status
)
=>
{
const
statusMap
=
{
正常运行
:
"success"
,
轻微异常
:
"warning"
,
故障
:
"danger"
,
};
return
statusMap
[
status
]
||
"info"
;
};
const
getHealthScoreColor
=
(
score
)
=>
{
// 去除百分号
const
value
=
parseInt
(
score
.
replace
(
"%"
,
""
));
if
(
value
>=
9
0
)
return
"#67C23A"
;
if
(
value
==
10
0
)
return
"#67C23A"
;
if
(
value
>=
70
)
return
"#E6A23C"
;
return
"#F56C6C"
;
};
...
...
@@ -302,7 +298,12 @@ const getIndex = (index) => {
};
const
handleView
=
(
row
)
=>
{
console
.
log
(
"查看详情"
,
row
);
router
.
push
({
path
:
"/monitor"
,
query
:
{
dusterNo
:
row
.
deviceNo
,
},
});
};
const
handleEdit
=
(
row
)
=>
{
...
...
@@ -357,6 +358,34 @@ const handleValveNumClick = (row) => {
}
};
const
handleStatusDotClick
=
(
row
,
colIndex
)
=>
{
router
.
push
({
path
:
"/bag-monitor"
,
query
:
{
dusterNo
:
row
.
deviceNo
,
dusterName
:
row
.
deviceName
,
}
});
};
const
handleDusterLeakNumClick
=
()
=>
{
router
.
push
({
path
:
"/alerts"
,
query
:
{
startTime
:
moment
().
format
(
"YYYY-MM-DD HH:mm:ss"
),
endTime
:
moment
().
subtract
(
24
,
'hours'
).
format
(
"YYYY-MM-DD HH:mm:ss"
),
},
});
};
const
handleCloseLoopNumClick
=
()
=>
{
router
.
push
({
path
:
"/my-loop/myAgency"
,
});
};
// 确认电磁阀设置
const
handleValveSettingConfirm
=
(
updatedStatusData
)
=>
{
const
list
=
updatedStatusData
.
map
((
item
)
=>
{
...
...
@@ -388,6 +417,10 @@ const setValveInfo = (data) => {
});
};
const
handleCancel
=
()
=>
{
isAddDustCollectorVisible
.
value
=
false
;
currentEditingDustCollector
.
value
=
null
;
};
// 保存新增或编辑除尘器
const
handleSaveDustCollector
=
(
data
)
=>
{
if
(
data
.
id
)
{
...
...
@@ -412,6 +445,7 @@ const handleSaveDustCollector = (data) => {
.
then
((
res
)
=>
{
if
(
res
.
code
==
1
)
{
ElMessage
.
success
(
"新增除尘器成功"
);
currentEditingDustCollector
.
value
=
null
;
isAddDustCollectorVisible
.
value
=
false
;
refreshData
();
}
else
{
...
...
@@ -540,6 +574,13 @@ onBeforeUnmount(() => {});
margin-top
:
10px
;
}
}
.crusor-click
{
cursor
:
pointer
;
transition
:
box-shadow
0
.3s
ease
;
// 添加过渡动画
&
:hover
{
box-shadow
:
0px
1
.33px
16px
0px
rgba
(
83
,
100
,
170
,
0
.5
);
}
}
}
.content-box
{
margin-top
:
24px
;
...
...
@@ -736,4 +777,14 @@ onBeforeUnmount(() => {});
}
}
}
:deep
(
.el-table
)
{
.el-table__column-resize-proxy
{
display
:
none
!
important
;
}
.el-table__header
th
{
user-select
:
none
;
}
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment