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
d5e2c6c8
Commit
d5e2c6c8
authored
Aug 12, 2025
by
Cai Wei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(*): 添加动画效果
parent
2efabdd5
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
567 additions
and
192 deletions
+567
-192
package-lock.json
package-lock.json
+11
-0
package.json
package.json
+1
-0
gsapNumber.vue
src/views/dustMonitoring/components/gsapNumber.vue
+343
-0
index.vue
src/views/dustMonitoring/index.vue
+212
-192
No files found.
package-lock.json
View file @
d5e2c6c8
...
...
@@ -13,6 +13,7 @@
"crypto-js"
:
"^4.2.0"
,
"echarts"
:
"^5.6.0"
,
"element-plus"
:
"^2.9.10"
,
"gsap"
:
"^3.13.0"
,
"js-cookie"
:
"^3.0.5"
,
"moment"
:
"^2.30.1"
,
"nprogress"
:
"^0.2.0"
,
...
...
@@ -1698,6 +1699,11 @@
"url"
:
"https://github.com/sponsors/ljharb"
}
},
"node_modules/gsap"
:
{
"version"
:
"3.13.0"
,
"resolved"
:
"https://registry.npmmirror.com/gsap/-/gsap-3.13.0.tgz"
,
"integrity"
:
"sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw=="
},
"node_modules/has-symbols"
:
{
"version"
:
"1.1.0"
,
"resolved"
:
"https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz"
,
...
...
@@ -3452,6 +3458,11 @@
"resolved"
:
"https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz"
,
"integrity"
:
"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
},
"gsap"
:
{
"version"
:
"3.13.0"
,
"resolved"
:
"https://registry.npmmirror.com/gsap/-/gsap-3.13.0.tgz"
,
"integrity"
:
"sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw=="
},
"has-symbols"
:
{
"version"
:
"1.1.0"
,
"resolved"
:
"https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz"
,
...
...
package.json
View file @
d5e2c6c8
...
...
@@ -14,6 +14,7 @@
"
crypto-js
"
:
"
^4.2.0
"
,
"
echarts
"
:
"
^5.6.0
"
,
"
element-plus
"
:
"
^2.9.10
"
,
"
gsap
"
:
"
^3.13.0
"
,
"
js-cookie
"
:
"
^3.0.5
"
,
"
moment
"
:
"
^2.30.1
"
,
"
nprogress
"
:
"
^0.2.0
"
,
...
...
src/views/dustMonitoring/components/gsapNumber.vue
0 → 100644
View file @
d5e2c6c8
<
template
>
<div
class=
"gsap-number-container"
>
<div
:class=
"['value', colorClass]"
ref=
"numberElement"
>
{{
isStringMode
?
stringValue
:
displayValue
}}
</div>
</div>
</
template
>
<
script
setup
>
import
{
ref
,
onMounted
,
watch
,
defineProps
,
defineEmits
,
onUnmounted
,
computed
}
from
'vue'
;
import
gsap
from
'gsap'
;
const
props
=
defineProps
({
// 要显示的目标数字或字符串
value
:
{
type
:
[
Number
,
String
],
required
:
true
,
default
:
0
},
// 数字的初始值
startValue
:
{
type
:
Number
,
default
:
0
},
// 动画持续时间(秒)
duration
:
{
type
:
Number
,
default
:
1
},
// 是否启用颜色变化
enableColorChange
:
{
type
:
Boolean
,
default
:
false
},
// 数字的小数位数
decimals
:
{
type
:
Number
,
default
:
0
},
// 数字的前缀
prefix
:
{
type
:
String
,
default
:
''
},
// 数字的后缀
suffix
:
{
type
:
String
,
default
:
''
},
// 是否在组件挂载时自动开始动画
autoStart
:
{
type
:
Boolean
,
default
:
true
},
// 动画类型
animationType
:
{
type
:
String
,
default
:
'count'
,
// 'count', 'bounce', 'color'
validator
:
(
value
)
=>
[
'count'
,
'bounce'
,
'color'
].
includes
(
value
)
},
// 颜色阈值(用于颜色变化)
colorThresholds
:
{
type
:
Object
,
default
:
()
=>
({
low
:
{
value
:
30
,
color
:
'#e74c3c'
},
// 红色
medium
:
{
value
:
70
,
color
:
'#f39c12'
},
// 橙色
high
:
{
value
:
100
,
color
:
'#36f1cd'
}
// 绿色
})
},
// 动画触发间隔(毫秒),默认为0表示不自动触发
animationInterval
:
{
type
:
Number
,
default
:
0
}
});
const
emit
=
defineEmits
([
'animation-complete'
]);
const
displayValue
=
ref
(
props
.
startValue
);
const
stringValue
=
ref
(
''
);
const
numberElement
=
ref
(
null
);
const
colorClass
=
ref
(
'blue'
);
// 默认颜色类
const
intervalTimer
=
ref
(
null
);
// 判断是否为字符串模式
const
isStringMode
=
computed
(()
=>
typeof
props
.
value
===
'string'
);
// 格式化数字显示
const
formatNumber
=
(
num
)
=>
{
// 判断传入的值有多少位小数
const
numStr
=
String
(
num
);
const
decimalParts
=
numStr
.
split
(
'.'
);
const
decimalPlaces
=
decimalParts
.
length
>
1
?
decimalParts
[
1
].
length
:
0
;
// 使用传入值的小数位数,而不是props.decimals
const
formattedNum
=
parseFloat
(
num
).
toFixed
(
decimalPlaces
);
return
`
${
props
.
prefix
}${
formattedNum
}${
props
.
suffix
}
`
;
};
// 根据值确定颜色
const
determineColor
=
(
value
)
=>
{
const
{
low
,
medium
,
high
}
=
props
.
colorThresholds
;
if
(
value
<=
low
.
value
)
return
'red'
;
if
(
value
<=
medium
.
value
)
return
'orange'
;
return
'green'
;
};
// 执行计数动画
const
animateCount
=
()
=>
{
// 如果是字符串模式,直接设置值并执行弹跳动画
if
(
isStringMode
.
value
)
{
stringValue
.
value
=
props
.
value
;
animateBounceForString
();
return
;
}
const
obj
=
{
value
:
props
.
startValue
};
// 保存目标值的小数位数
const
targetValueStr
=
String
(
props
.
value
);
const
decimalParts
=
targetValueStr
.
split
(
'.'
);
const
targetDecimalPlaces
=
decimalParts
.
length
>
1
?
decimalParts
[
1
].
length
:
0
;
gsap
.
to
(
obj
,
{
value
:
props
.
value
,
duration
:
props
.
duration
,
ease
:
"power2.inOut"
,
onUpdate
:
function
()
{
// 动画过程中使用整数递增
const
intValue
=
Math
.
floor
(
obj
.
value
);
displayValue
.
value
=
`
${
props
.
prefix
}${
intValue
}${
props
.
suffix
}
`
;
if
(
props
.
enableColorChange
)
{
colorClass
.
value
=
determineColor
(
obj
.
value
);
}
},
onComplete
:
()
=>
{
// 动画完成时显示原始值的完整格式(包含小数)
displayValue
.
value
=
formatNumber
(
props
.
value
);
emit
(
'animation-complete'
);
}
});
};
// 执行弹跳动画
const
animateBounce
=
()
=>
{
// 如果是字符串模式,使用字符串弹跳动画
if
(
isStringMode
.
value
)
{
stringValue
.
value
=
props
.
value
;
animateBounceForString
();
return
;
}
// 首先更新值
displayValue
.
value
=
formatNumber
(
props
.
value
);
// 然后添加弹跳动画
const
tl
=
gsap
.
timeline
();
tl
.
from
(
numberElement
.
value
,
{
scale
:
0.5
,
duration
:
0.5
,
ease
:
"back.out(1.7)"
});
tl
.
to
(
numberElement
.
value
,
{
scale
:
1.2
,
duration
:
0.3
,
ease
:
"power2.inOut"
});
tl
.
to
(
numberElement
.
value
,
{
scale
:
1
,
duration
:
0.3
,
ease
:
"power2.inOut"
,
onComplete
:
()
=>
{
emit
(
'animation-complete'
);
}
});
};
// 字符串的弹跳动画
const
animateBounceForString
=
()
=>
{
const
tl
=
gsap
.
timeline
();
tl
.
from
(
numberElement
.
value
,
{
scale
:
0.5
,
duration
:
0.5
,
ease
:
"back.out(1.7)"
});
tl
.
to
(
numberElement
.
value
,
{
scale
:
1.1
,
duration
:
0.3
,
ease
:
"power2.inOut"
});
tl
.
to
(
numberElement
.
value
,
{
scale
:
1
,
duration
:
0.3
,
ease
:
"power2.inOut"
,
onComplete
:
()
=>
{
emit
(
'animation-complete'
);
}
});
};
// 执行颜色变化动画
const
animateColor
=
()
=>
{
// 如果是字符串模式,使用字符串弹跳动画
if
(
isStringMode
.
value
)
{
stringValue
.
value
=
props
.
value
;
animateBounceForString
();
return
;
}
// 首先执行计数动画
const
obj
=
{
value
:
props
.
startValue
};
// 确定目标颜色
const
targetColor
=
props
.
enableColorChange
?
props
.
colorThresholds
[
determineColor
(
props
.
value
)].
color
:
'#36f1cd'
;
gsap
.
to
(
obj
,
{
value
:
props
.
value
,
duration
:
props
.
duration
,
ease
:
"power2.inOut"
,
onUpdate
:
function
()
{
// 动画过程中使用整数递增
const
intValue
=
Math
.
floor
(
obj
.
value
);
displayValue
.
value
=
`
${
props
.
prefix
}${
intValue
}${
props
.
suffix
}
`
;
},
onComplete
:
()
=>
{
// 动画完成时显示原始值的完整格式(包含小数)
displayValue
.
value
=
formatNumber
(
props
.
value
);
}
});
// 同时执行颜色变化
gsap
.
to
(
numberElement
.
value
,
{
color
:
targetColor
,
duration
:
props
.
duration
,
ease
:
"power2.inOut"
,
onComplete
:
()
=>
{
emit
(
'animation-complete'
);
}
});
};
// 开始动画
const
startAnimation
=
()
=>
{
switch
(
props
.
animationType
)
{
case
'bounce'
:
animateBounce
();
break
;
case
'color'
:
animateColor
();
break
;
case
'count'
:
default
:
animateCount
();
break
;
}
};
// 设置定时器
const
setupIntervalTimer
=
()
=>
{
// 清除之前的定时器
if
(
intervalTimer
.
value
)
{
clearInterval
(
intervalTimer
.
value
);
intervalTimer
.
value
=
null
;
}
// 如果设置了有效的间隔时间,则创建新的定时器
if
(
props
.
animationInterval
>
0
)
{
intervalTimer
.
value
=
setInterval
(()
=>
{
startAnimation
();
},
props
.
animationInterval
);
}
};
// 监听值变化
watch
(()
=>
props
.
value
,
(
newValue
)
=>
{
// 如果是字符串,直接设置值
if
(
typeof
newValue
===
'string'
)
{
stringValue
.
value
=
newValue
;
}
startAnimation
();
});
// 监听间隔时间变化
watch
(()
=>
props
.
animationInterval
,
()
=>
{
setupIntervalTimer
();
});
// 组件挂载时
onMounted
(()
=>
{
// 如果初始值是字符串,直接设置
if
(
typeof
props
.
value
===
'string'
)
{
stringValue
.
value
=
props
.
value
;
}
if
(
props
.
autoStart
)
{
startAnimation
();
}
// 设置定时器(如果有指定间隔)
setupIntervalTimer
();
});
// 组件卸载时清除定时器
onUnmounted
(()
=>
{
if
(
intervalTimer
.
value
)
{
clearInterval
(
intervalTimer
.
value
);
intervalTimer
.
value
=
null
;
}
});
</
script
>
<
style
scoped
>
.gsap-number-container
{
width
:
100%
;
}
.value
{
transition
:
color
0.3s
ease
;
}
.blue
{
color
:
#fff
;
}
.green
{
color
:
#fff
;
}
.orange
{
color
:
#fff
;
}
.red
{
color
:
#fff
;
}
</
style
>
\ No newline at end of file
src/views/dustMonitoring/index.vue
View file @
d5e2c6c8
<
template
>
<div
class=
"dust-box"
ref=
"dustBox"
>
<div
class=
"top-box"
>
<el-form
:model=
"form"
label-width=
"auto"
:inline=
"true"
class=
"demo-form-inline"
>
<el-form
:model=
"form"
label-width=
"auto"
:inline=
"true"
class=
"demo-form-inline"
>
<el-form-item
label=
"除尘器名称"
>
<el-select
v-model=
"form.dusterNo"
placeholder=
"请选择除尘器"
style=
"width: 240px"
filterable
>
<el-option
v-for=
"item in dusterList"
:key=
"item.dusterNo"
:label=
"item.dusterName"
:value=
"item.dusterNo"
></el-option>
<el-select
v-model=
"form.dusterNo"
placeholder=
"请选择除尘器"
style=
"width: 240px"
filterable
>
<el-option
v-for=
"item in dusterList"
:key=
"item.dusterNo"
:label=
"item.dusterName"
:value=
"item.dusterNo"
></el-option>
</el-select>
</el-form-item>
<el-form-item
label=
"分析时间"
>
<el-date-picker
v-model=
"form.dateValue"
type=
"datetimerange"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 450px"
popper-class=
"date-picker-popper"
@
calendar-change=
"calendarChange"
@
visible-change=
"visibleChange"
:disabled-date=
"disabledFn"
/>
<el-date-picker
v-model=
"form.dateValue"
type=
"datetimerange"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
style=
"width: 450px"
popper-class=
"date-picker-popper"
@
calendar-change=
"calendarChange"
@
visible-change=
"visibleChange"
:disabled-date=
"disabledFn"
/>
</el-form-item>
</el-form>
</div>
...
...
@@ -45,11 +23,7 @@
</div>
</div>
<div
class=
"warn-info"
>
<warnCom
title=
"告警"
:listInfo=
"warnInfoList"
@
jumpPage=
"toWarnDetail"
></warnCom>
<warnCom
title=
"告警"
:listInfo=
"warnInfoList"
@
jumpPage=
"toWarnDetail"
></warnCom>
</div>
</div>
<div
class=
"right-box"
>
...
...
@@ -63,9 +37,9 @@
<div
class=
"dust-info"
>
<div
class=
"info-item"
v-for=
"item in dustInfo"
:key=
"item.label"
>
<span
class=
"label"
>
{{
item
.
label
}}
</span>
<span
class=
"value"
>
{{
item
.
value
}}
<span
class=
"unit"
>
(
{{
item
.
unit
}}
)
</span
>
<span
class=
"value"
>
<gsapNumber
:value=
"item.value"
:animationType=
"'count'"
:animationInterval=
"10000"
/>
<span
class=
"unit"
>
(
{{
item
.
unit
}}
)
</span
>
</span>
</div>
<div></div>
...
...
@@ -76,22 +50,13 @@
</div>
<div
class=
"position-info"
>
<div
class=
"left"
v-if=
"detailObj.compartHealthList.length > 0"
>
<div
class=
"part"
v-for=
"(list, index) in detailObj.compartHealthList"
:key=
"index"
>
<div
class=
"point"
:class=
"
{
'status-normal': item.healthStatus === 1,
'status-error': item.healthStatus === 2,
'status-warning': item.healthStatus === 3,
}"
v-for="item in detailObj.compartHealthList[index]"
:key="item"
@click="handleStatusDotClick()"
>
</div>
<div
class=
"part"
v-for=
"(list, index) in detailObj.compartHealthList"
:key=
"index"
>
<div
class=
"point"
:class=
"
{
'status-normal': item.healthStatus === 1,
'status-error': item.healthStatus === 2,
'status-warning': item.healthStatus === 3,
}" v-for="item in detailObj.compartHealthList[index]" :key="item" @click="handleStatusDotClick()">
</div>
</div>
</div>
<!--
<div
class=
"right"
>
...
...
@@ -119,11 +84,7 @@
</div>
</div>
<div
class=
"warn-info"
>
<warnCom
title=
"闭环"
:listInfo=
"closedLoopInfoList"
@
jumpPage=
"toCircleDetail"
></warnCom>
<warnCom
title=
"闭环"
:listInfo=
"closedLoopInfoList"
@
jumpPage=
"toCircleDetail"
></warnCom>
</div>
</div>
</div>
...
...
@@ -139,6 +100,7 @@ import moment from "moment";
import
warnCom
from
"./components/warn.vue"
;
import
healthyCom
from
"./components/healthyProgress.vue"
;
import
{
getLineOption2
,
getGaugeOption
}
from
"@/utils/chart"
;
import
gsapNumber
from
"./components/gsapNumber.vue"
;
const
userStore
=
useUsersStore
();
const
form
=
reactive
({
...
...
@@ -170,33 +132,19 @@ const disabledFn = (current) => {
let
cur
=
new
Date
(
current
).
getTime
();
return
cur
<
range
[
0
]
||
cur
>
range
[
1
];
};
const
option
=
{
xAxis
:
{
type
:
"category"
,
data
:
[
"Mon"
,
"Tue"
,
"Wed"
,
"Thu"
,
"Fri"
,
"Sat"
,
"Sun"
],
},
yAxis
:
{
type
:
"value"
,
},
series
:
[
{
data
:
[
820
,
932
,
901
,
934
,
1290
,
1330
,
1320
],
type
:
"line"
,
smooth
:
true
,
},
],
};
const
router
=
useRouter
();
const
route
=
useRoute
();
const
chartInstance
=
reactive
({});
const
chartData
=
reactive
([{},
{},
{}]);
const
initChart
=
(
instance
,
option
,
domEl
)
=>
{
if
(
instance
&&
instance
.
dispose
)
{
instance
.
dispose
();
// 检查是否已经存在该dom的实例,如果存在则清除
if
(
chartInstance
[
domEl
])
{
chartInstance
[
domEl
].
dispose
();
}
instance
=
echarts
.
init
(
document
.
getElementById
(
domEl
));
instance
.
setOption
(
option
);
chartInstance
[
domEl
]
=
instance
;
// 创建新的echarts实例
const
newInstance
=
echarts
.
init
(
document
.
getElementById
(
domEl
));
newInstance
.
setOption
(
option
);
chartInstance
[
domEl
]
=
newInstance
;
chartLinkageFun
();
};
let
instanceList
=
reactive
([]);
...
...
@@ -291,7 +239,7 @@ const dustInfo = reactive([
unitKey
:
"pressureDiffUnit"
,
},
{
label
:
"
粉尘
浓度"
,
label
:
"
排口
浓度"
,
value
:
"0.0"
,
unit
:
"mg/m³"
,
key
:
"dustConcentration"
,
...
...
@@ -310,8 +258,8 @@ const dustInfo = reactive([
const
indicatorOneInstance
=
ref
();
const
indicatorTwoInstance
=
ref
();
const
indicatorFun
=
(
target
,
domId
,
option
)
=>
{
if
(
target
.
value
&&
target
.
dispose
)
{
target
.
value
.
dispose
();
if
(
target
.
value
)
{
target
.
value
.
clear
();
}
target
.
value
=
echarts
.
init
(
document
.
getElementById
(
domId
));
target
.
value
.
setOption
(
option
);
...
...
@@ -424,6 +372,32 @@ const getDustDetail = () => {
startTime
:
moment
(
form
.
dateValue
[
0
]).
format
(
"YYYY-MM-DD"
)
+
" 00:00:00"
,
endTime
:
moment
(
form
.
dateValue
[
1
]).
format
(
"YYYY-MM-DD"
)
+
" 23:59:59"
,
};
dustInfo
.
value
=
[
{
label
:
"压差"
,
value
:
"0.0"
,
unit
:
"kPa"
,
key
:
"pressureDiff"
,
unitKey
:
"pressureDiffUnit"
,
},
{
label
:
"排口浓度"
,
value
:
"0.0"
,
unit
:
"mg/m³"
,
key
:
"dustConcentration"
,
unitKey
:
"dustConcentrationUnit"
,
},
{
label
:
"电磁阀数量"
,
value
:
"0"
,
unit
:
"个"
,
key
:
"valveNum"
},
{
label
:
"仓室数量"
,
value
:
"0"
,
unit
:
"个"
,
key
:
"compartNum"
},
{
label
:
"布袋检测仪数量"
,
value
:
"0"
,
unit
:
"个"
,
key
:
"bagDetectorNum"
},
{
label
:
"脉冲检测仪数量"
,
value
:
"0"
,
unit
:
"个"
,
key
:
"pulseDetectorNum"
,
},
];
getDataFun
(
url
,
params
)
.
then
((
res
)
=>
{
if
(
res
&&
res
.
data
)
{
...
...
@@ -445,82 +419,82 @@ const getDustDetail = () => {
detailObj
.
faultCompart
=
(
res
.
data
&&
res
.
data
.
faultCompart
)
||
""
;
detailObj
.
compartHealthList
=
(
res
.
data
&&
res
.
data
.
compartHealthList
)
||
[
[
{
compartNo
:
"1"
,
healthStatus
:
1
,
sort
:
1
,
},
{
compartNo
:
"2"
,
healthStatus
:
1
,
sort
:
2
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
3
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
4
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
5
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
6
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
7
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
8
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
9
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
10
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
11
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
12
,
},
],
];
[
{
compartNo
:
"1"
,
healthStatus
:
1
,
sort
:
1
,
},
{
compartNo
:
"2"
,
healthStatus
:
1
,
sort
:
2
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
3
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
4
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
5
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
6
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
7
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
8
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
9
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
10
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
11
,
},
{
compartNo
:
null
,
healthStatus
:
1
,
sort
:
12
,
},
],
];
indicatorFun
(
indicatorOneInstance
,
"indicatorOne"
,
getGaugeOption
({
value
:
dustInfo
[
0
].
value
.
toFixed
(
2
),
chartName
:
"压差"
+
'('
+
dustInfo
[
0
].
unit
+
')'
,
chartName
:
"压差"
+
'('
+
dustInfo
[
0
].
unit
+
')'
,
min
:
parseInt
(
res
.
data
.
pressureDiffLow
),
max
:
parseInt
(
res
.
data
.
pressureDiffHigh
),
formatter
:
parseInt
(
res
.
data
.
pressureDiffHigh
)
>
10
?
function
(
value
)
{
return
parseInt
(
value
);
}
return
parseInt
(
value
);
}
:
""
,
})
);
...
...
@@ -529,9 +503,9 @@ const getDustDetail = () => {
"indicatorTwo"
,
getGaugeOption
({
value
:
parseFloat
(
dustInfo
[
1
].
value
),
chartName
:
"
粉尘浓度"
+
'('
+
dustInfo
[
1
].
unit
+
')'
,
min
:
5
,
max
:
parseInt
(
dustInfo
[
1
].
value
)
>
50
?
Math
.
ceil
(
parseFloat
(
dustInfo
[
1
].
value
)
/
10
)
*
10
:
5
0
,
chartName
:
"
排口浓度"
+
'('
+
dustInfo
[
1
].
unit
+
')'
,
min
:
0
,
max
:
parseInt
(
dustInfo
[
1
].
value
)
>
50
?
Math
.
ceil
(
parseFloat
(
dustInfo
[
1
].
value
)
/
10
)
*
10
:
1
0
,
formatter
:
function
(
value
)
{
return
parseInt
(
value
);
},
...
...
@@ -618,24 +592,12 @@ onUnmounted(() => {
watch
(
()
=>
form
.
dusterNo
,
()
=>
{
getHealthIndex
();
getPressure
();
getEnergy
();
getDustDetail
();
getAlarmInfo
();
// 重新启动实时监测
startRealTimeMonitoring
();
}
);
watch
(
()
=>
form
.
dateValue
,
()
=>
{
getHealthIndex
();
getPressure
();
getEnergy
();
getDustDetail
();
getAlarmInfo
();
// 重新启动实时监测
startRealTimeMonitoring
();
}
);
...
...
@@ -676,9 +638,23 @@ let realTimeTimer = null;
const
startRealTimeMonitoring
=
()
=>
{
if
(
realTimeTimer
)
{
clearInterval
(
realTimeTimer
);
cancelAllMouseEvent
();
chartRemoveEvent
();
}
// 默认先触发一次
getDustDetail
();
getHealthIndex
();
getPressure
();
getEnergy
();
getDustDetail
();
getAlarmInfo
();
realTimeTimer
=
setInterval
(()
=>
{
getDustDetail
();
getHealthIndex
();
getPressure
();
getEnergy
();
getDustDetail
();
getAlarmInfo
();
},
10000
);
}
...
...
@@ -695,8 +671,8 @@ const getAllMouseEvent = () => {
dustBox
.
value
.
addEventListener
(
"mousedown"
,
cancelRefreshEvent
)
}
const
cancelRefreshEvent
=
()
=>
{
clearTimeout
(
refreshTimer
);
refeshFun
();
clearTimeout
(
refreshTimer
);
refeshFun
();
}
// 取消鼠标事件
const
cancelAllMouseEvent
=
()
=>
{
...
...
@@ -709,6 +685,7 @@ const cancelAllMouseEvent = () => {
</
script
>
<
style
lang=
"scss"
scoped
>
$borderColor
:
#3a3f45
;
.dust-box
{
width
:
100%
;
height
:
calc
(
100%
-
14px
);
...
...
@@ -720,11 +697,13 @@ $borderColor: #3a3f45;
flex-direction
:
column
;
// background-color: #181d21;
color
:
#909399
;
&
:
:-
webkit-scrollbar
{
width
:
2px
;
background-color
:
rgba
(
58
,
63
,
69
,
0
.5
);
}
}
.top-box
{
width
:
100%
;
padding-top
:
18px
;
...
...
@@ -735,8 +714,10 @@ $borderColor: #3a3f45;
box-shadow
:
0px
3px
6px
0px
rgba
(
0
,
0
,
0
,
0
.2
);
margin-bottom
:
1rem
;
}
.left-box
{
width
:
50%
;
.part1
{
border-radius
:
6px
;
border
:
1px
solid
rgba
(
58
,
63
,
69
,
0
.5
);
...
...
@@ -744,9 +725,11 @@ $borderColor: #3a3f45;
width
:
100%
;
height
:
63vh
;
background
:
#181d21
;
.chart-box
{
width
:
100%
;
height
:
31%
;
.chart-item
{
width
:
100%
;
height
:
100%
;
...
...
@@ -756,14 +739,17 @@ $borderColor: #3a3f45;
}
.value
{
color
:
#fff
;
font-size
:
14px
;
}
color
:
#fff
;
font-size
:
14px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
.unit
{
color
:
#909399
;
font-size
:
12px
;
}
.unit
{
color
:
#909399
;
font-size
:
12px
;
}
.right-box
{
width
:
50%
;
...
...
@@ -777,11 +763,13 @@ $borderColor: #3a3f45;
background
:
#181d21
;
padding
:
1%
;
box-sizing
:
border-box
;
.battery
{
width
:
100%
;
height
:
calc
(
100%
);
}
}
.part2
{
margin-top
:
10px
;
width
:
100%
;
...
...
@@ -795,6 +783,7 @@ $borderColor: #3a3f45;
box-sizing
:
border-box
;
background
:
#181d21
;
position
:
relative
;
.dust-title
{
text-align
:
center
;
font-size
:
20px
;
...
...
@@ -804,6 +793,7 @@ $borderColor: #3a3f45;
width
:
100%
;
color
:
#fff
;
}
.dust-info
{
padding
:
0
.5rem
1rem
;
background
:
#232a31
;
...
...
@@ -813,6 +803,7 @@ $borderColor: #3a3f45;
flex-wrap
:
wrap
;
gap
:
0
10px
;
}
.info-item
{
padding
:
3px
0
;
width
:
calc
(
50%
-
5px
);
...
...
@@ -827,24 +818,29 @@ $borderColor: #3a3f45;
align-items
:
center
;
font-weight
:
600
;
justify-content
:
space-between
;
.label
{
color
:
#909399
;
}
}
.indicator-box
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
.indicator-item
{
width
:
50%
;
height
:
180px
;
}
}
.position-info
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
padding
:
10px
20px
20px
20px
;
.left
,
.right
{
padding
:
5px
;
...
...
@@ -852,17 +848,20 @@ $borderColor: #3a3f45;
flex-wrap
:
wrap
;
box-shadow
:
rgba
(
0
,
0
,
0
,
0
.3
)
0px
0px
20px
0px
;
}
.left
{
background-color
:
#232a31
;
border-radius
:
6px
;
padding
:
10px
;
box-sizing
:
border-box
;
}
.part
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-around
;
}
.point
{
width
:
20px
;
margin
:
10px
;
...
...
@@ -870,16 +869,20 @@ $borderColor: #3a3f45;
display
:
inline-block
;
cursor
:
pointer
;
transition
:
transform
0
.3s
ease-in-out
;
&
:hover
{
transform
:
scale
(
1
.2
);
box-shadow
:
0
0
0px
0
rgba
(
255
,
255
,
255
,
0
.5
);
}
}
.
point
:
:
after
{
content
:
""
;
display
:
block
;
padding-bottom
:
100%
;
/* 9/16 * 100% = 56.25% */
padding-bottom
:
100%
;
/* 9/16 * 100% = 56.25% */
}
.status-normal
{
background-color
:
rgb
(
103
,
194
,
58
);
// 绿色
animation
:
pulse-glow1
2s
ease-in-out
infinite
;
...
...
@@ -898,56 +901,64 @@ $borderColor: #3a3f45;
@keyframes
pulse-glow1
{
0
%
{
box-shadow
:
0
0
0px
rgba
(
103
,
194
,
58
,
0
.3
)
,
0
0
10px
rgba
(
103
,
194
,
58
,
0
.2
)
,
0
0
20px
rgba
(
103
,
194
,
58
,
0
.1
);
0
0
10px
rgba
(
103
,
194
,
58
,
0
.2
)
,
0
0
20px
rgba
(
103
,
194
,
58
,
0
.1
);
}
50
%
{
box-shadow
:
0
0
20px
rgba
(
103
,
194
,
58
,
0
.8
)
,
0
0
10px
rgba
(
103
,
194
,
58
,
0
.6
)
,
0
0
20px
rgba
(
103
,
194
,
58
,
0
.4
);
0
0
10px
rgba
(
103
,
194
,
58
,
0
.6
)
,
0
0
20px
rgba
(
103
,
194
,
58
,
0
.4
);
}
100
%
{
box-shadow
:
0
0
0px
rgba
(
103
,
194
,
58
,
0
.3
)
,
0
0
10px
rgba
(
103
,
194
,
58
,
0
.2
)
,
0
0
20px
rgba
(
103
,
194
,
58
,
0
.1
);
0
0
10px
rgba
(
103
,
194
,
58
,
0
.2
)
,
0
0
20px
rgba
(
103
,
194
,
58
,
0
.1
);
}
}
@keyframes
pulse-glow2
{
0
%
{
box-shadow
:
0
0
0px
rgba
(
230
,
162
,
60
,
0
.3
)
,
0
0
10px
rgba
(
230
,
162
,
60
,
0
.2
)
,
0
0
20px
rgba
(
230
,
162
,
60
,
0
.1
);
0
0
10px
rgba
(
230
,
162
,
60
,
0
.2
)
,
0
0
20px
rgba
(
230
,
162
,
60
,
0
.1
);
}
50
%
{
box-shadow
:
0
0
0px
rgba
(
230
,
162
,
60
,
0
.8
)
,
0
0
10px
rgba
(
230
,
162
,
60
,
0
.6
)
,
0
0
20px
rgba
(
230
,
162
,
60
,
0
.4
);
0
0
10px
rgba
(
230
,
162
,
60
,
0
.6
)
,
0
0
20px
rgba
(
230
,
162
,
60
,
0
.4
);
}
100
%
{
box-shadow
:
0
0
0px
rgba
(
230
,
162
,
60
,
0
.3
)
,
0
0
10px
rgba
(
230
,
162
,
60
,
0
.2
)
,
0
0
20px
rgba
(
230
,
162
,
60
,
0
.1
);
0
0
10px
rgba
(
230
,
162
,
60
,
0
.2
)
,
0
0
20px
rgba
(
230
,
162
,
60
,
0
.1
);
}
}
@keyframes
pulse-glow3
{
0
%
{
box-shadow
:
0
0
0px
rgba
(
245
,
108
,
108
,
0
.3
)
,
0
0
10px
rgba
(
245
,
108
,
108
,
0
.2
)
,
0
0
20px
rgba
(
245
,
108
,
108
,
0
.1
);
0
0
10px
rgba
(
245
,
108
,
108
,
0
.2
)
,
0
0
20px
rgba
(
245
,
108
,
108
,
0
.1
);
}
50
%
{
box-shadow
:
0
0
0px
rgba
(
245
,
108
,
108
,
0
.8
)
,
0
0
10px
rgba
(
245
,
108
,
108
,
0
.6
)
,
0
0
20px
rgba
(
245
,
108
,
108
,
0
.4
);
0
0
10px
rgba
(
245
,
108
,
108
,
0
.6
)
,
0
0
20px
rgba
(
245
,
108
,
108
,
0
.4
);
}
100
%
{
box-shadow
:
0
0
0px
rgba
(
245
,
108
,
108
,
0
.3
)
,
0
0
10px
rgba
(
245
,
108
,
108
,
0
.2
)
,
0
0
20px
rgba
(
245
,
108
,
108
,
0
.1
);
0
0
10px
rgba
(
245
,
108
,
108
,
0
.2
)
,
0
0
20px
rgba
(
245
,
108
,
108
,
0
.1
);
}
}
}
.other-info
{
padding
:
20px
;
border-top
:
1px
solid
$borderColor
;
...
...
@@ -956,6 +967,7 @@ $borderColor: #3a3f45;
justify-content
:
space-between
;
font-size
:
14px
;
gap
:
10px
;
&
-item
{
padding
:
10px
;
min-height
:
70px
;
...
...
@@ -963,26 +975,31 @@ $borderColor: #3a3f45;
border-radius
:
6px
;
flex
:
1
;
overflow-y
:
auto
;
p
:nth-child
(
2
)
{
font-size
:
14px
;
font-weight
:
600
;
color
:
#fff
;
}
}
&
-item
:
:-
webkit-scrollbar
{
width
:
0
;
}
.label
{
color
:
#909399
;
}
}
}
.
part2
:
:-
webkit-scrollbar
{
width
:
2px
;
background-color
:
rgba
(
58
,
63
,
69
,
0
.5
);
}
}
.warn-info
{
margin-top
:
20px
;
height
:
17vh
;
...
...
@@ -991,17 +1008,20 @@ $borderColor: #3a3f45;
padding
:
10px
30px
;
background
:
#181d21
;
}
.layout1
{
display
:
flex
;
justify-content
:
space-between
;
gap
:
16px
;
flex
:
1
;
}
.layout2
{
display
:
flex
;
justify-content
:
space-between
;
flex-direction
:
column
;
}
.layout3
{
display
:
flex
;
align-items
:
center
;
...
...
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