1.自定义指令部分
export default {
inserted(el, binding) {
const domHeight = el.clientHeight;
let distance = Number(binding.arg)
if (Number.isNaN(distance)) distance = 0
const { immediate } = binding.modifiers
const cb = binding.value
if (el.scrollHeight === 0) {
console.log(`scollheigt == ${el.scrollHeight}, 请检查overflow属性`);
}
const handleScroll = () => {
if (el.scrollHeight - el.scrollTop - distance <= domHeight) {
console.log(el.scrollTop, 'bottom!');
cb()
}
}
el['ifsScope'] = { handleScroll }
el.addEventListener('scroll', handleScroll)
// childList:子节点的变动。
// attributes:属性的变动。
// characterData:节点内容或节点文本的变动。
// subtree:所有后代节点的变动。
if (immediate) {
const observer = new MutationObserver(handleScroll)
observer.observe(el, { childList: false, subtree: false })
handleScroll()
}
},
unbind(el) {
const { handleScroll } = el['ifsScope']
el.removeEventListener('scroll', handleScroll)
}
}
2.页面使用部分
<template>
<div
class="recycle-scroller-demo"
:class="{
'page-mode': pageMode,
'full-page': pageModeFullPage,
}"
>
<div class="container">
<div class="header-search-wrap">
<div class="mr20">
<span class="text-style">时间范围</span>
<el-date-picker
v-model="searchObj.timevalue"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</div>
<div class="mr20">
<span v-if="type != 'PARK'" class="text-style">所属区域</span>
<el-cascader
v-if="type !== 'PARK'"
class="my-style"
popper-class="site-cascader"
style="width: 200px;height:36px;margin-right:10px"
ref="casca"
size="small"
v-model="searchObj.treeIds"
:options="treeData"
:props="{ checkStrictly: false, value: 'id', label: 'name', children: 'childInfoVOList', emitPath: false, multiple: true }"
clearable>
</el-cascader>
</div>
<div class="mr20">
<span class="text-style">项目名称</span>
<el-input clearable v-model="searchObj.programName" placeholder="搜索项目名称" width="200"></el-input>
</div>
<div class="mr20">
<span class="text-style" style="line-height: 36px;">污染程度</span><el-select v-model="searchObj.quality" placeholder="污染程度">
<el-option
v-for="item in optonsArr"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="mr20">
<el-button type="primary" size="mini" @click="fetchTableData">查 询</el-button>
<el-button type="primary" size="mini" @click="resetClick">重 置</el-button>
<el-button type="primary" size="mini" @click="exportTableData">导出数据</el-button>
</div>
</div>
<div class="legend-wrap">
<dl>
<dd v-for="(item,index) in Object.keys(colorObj)" :key="index">
<span>{{item}}</span><b :class="colorObj[item]"></b>
</dd>
</dl>
</div>
<div v-loading="loading" element-loading-text="拼命加载中"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)" class="table-wrap" v-directive-list:[50]="getDataMy">
<dl class="table-head">
<dt>序号</dt>
<dt>区域</dt>
<dt>项目名称</dt>
<dt>PM10(μg/㎡)</dt>
<dt>PM2.5</dt>
<dt>统计时间</dt>
<dt>污染程度</dt>
</dl>
<dl class="table-body" v-for="(item,index) in items" :key="index" :class="colorObj[item.airQuality]" >
<dd>{{index + 1}}</dd>
<dd >{{item.area}}</dd>
<dd @click="handleClickDialog(item)" style="cursor:pointer;">{{item.programName}}</dd>
<dd>{{item.pm10}}</dd>
<dd>{{item.pm25}}</dd>
<dd>{{item.statisticsTime}}</dd>
<dd>{{item.airQuality}}</dd>
</dl>
</div>
</div>
<MyDialog ref="myDialog" :dialogTitle ="dialogTitle" dialogWidth="90%">
<GZNormalWrap style="background-color: #031439">
<div class="header-search-wrap">
<el-date-picker
v-model="searchObj.timevalue"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
<el-button type="primary" size="mini" @click="fetchTableData" style="margin-left:20px">查 询</el-button>
</div>
<div style="width: 100%; min-height:500px; display: flex; flex-direction: column">
<div class="dialog-content">
<div style="flex: 1; margin-top: 12px;">
<chart :options="options" auto-resize class="mycharts2"></chart>
</div>
</div>
</div>
</GZNormalWrap>
</MyDialog>
</div>
</template>
<script>
import api from 'api';
import GetUrlParams from 'mixins/getUrlParams'
import GZNormalWrap from '../../common/GZNormalWrap';
import MyDialog from 'plugin/MyDialog';
import directiveList from 'utils/directiveList';
import Axios from 'axios'
export default {
directives: { directiveList },
data() {
return {
searchObj: {
timevalue: [new Date().getTime() - 2 * 60 * 60 * 1000, new Date().getTime()],
areaCodes: [],
programName: '',
quality: ''
},
dialogVisible: false,
dialogTitle: '',
detailObj: {
timevalue: [new Date().getTime() - 2 * 60 * 60 * 1000, new Date().getTime()]
},
loading: false,
pageNo: 1,
pageSize: 30,
treeData: [],
colorObj: { '优': 'level-class5', '良': 'level-class4', '轻度污染': 'level-class3', '中度污染': 'level-class2', '重度污染': 'level-class1', '严重污染': 'level-class', '0值': 'level-class6', '离线': 'level-class7' },
count: 10000,
renderScroller: true,
showScroller: true,
scopedSlots: false,
buffer: 200,
poolSize: 2000,
enableLetters: true,
pageMode: false,
totalCount: 0,
pageModeFullPage: true,
options: {
tooltip: {
trigger: 'axis'
},
legend: {
left: 'right',
top: 0,
textStyle: {
color: 'fff',
fontSize: 12
}
},
grid: {
left: 20,
right: 10,
bottom: 30,
top: 30,
containLabel: true
},
xAxis: {
type: 'category',
axisLabel: {
show: true,
// interval: 0,
// rotate: -30,
fontSize: 12,
formatter: item => {
if(item.split(' ').length > 1) {
return item.replace(/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/g, '$4')
} else {
return item.replace(/(\d{4})-(\d{2})-/g, '')
}
},
textStyle: {
color: '#fff'
}
},
axisLine: {
// onZero: true,
lineStyle: {
color: '#0D85B4'
}
},
axisTick: {
show: false
},
data: []
},
yAxis: {
name: 'PM10',
nameTextStyle: {
color: '#FFFFFF'
},
type: 'value',
axisLabel: {
show: true,
fontSize: 12,
textStyle: {
color: '#fff'
}
},
axisLine: {
onZero: true,
lineStyle: {
color: '#00C6FF'
}
},
axisTick: {
show: false
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed',
color: '#00C6FF',
opacity: 0.3
}
}
},
color: ['#3399FF', '#61DDAA', '#65789B', '#F6BD16'],
series: [
{
name: 'PM10',
data: [],
type: 'line',
symbol: 'circle',
smooth: true,
symbolSize: 10
},
{
name: 'PM25',
data: [],
type: 'line',
symbol: 'circle',
smooth: true,
symbolSize: 10
}
]
},
items: [
],
qualityObj: { '优': 'GOOD', '良': 'MODERATE', '轻度污染': 'LIGHTLY_POLLUTED', '中度污染': 'MODERATELY_POLLUTED', '严重污染': 'SEVERELY_POLLUTED', '重度污染': 'HEAVILY_POLLUTED', '0值': 'ZERO', '离线': 'OFFLINE' },
optonsArr: [{ label: '全部', value: '' }, { label: '优', value: 'GOOD' }, { label: '良', value: 'MODERATE' }, { label: '轻度污染', value: 'LIGHTLY_POLLUTED' }, { label: '中度污染', value: 'MODERATELY_POLLUTED' }, { label: '重度污染', value: 'HEAVILY_POLLUTED' }, { label: '严重污染', value: 'SEVERELY_POLLUTED' }, { label: '0值', value: 'ZERO' }, { label: '离线', value: 'OFFLINE' }]
}
},
mixins: [GetUrlParams],
components: {
GZNormalWrap,
MyDialog
},
mounted() {
this.getChildExcludePark()
window.scroller = this.$refs.scroller
this.getDustMonitorOverview()
this.intervalId = setInterval(() => {
this.pageNo = 1
this.getDustMonitorOverview()
// clearInterval(intervalId);
}, 5 * 60 * 1000);
},
destroyed() {
clearInterval(this.intervalId)
},
methods: {
getChildExcludePark() {
const type = this.type;
const id = this.id;
this.$get(api.getChildExcludePark(this.globalTenantId, this.type, this.id), {
tenantId: this.globalTenantId,
type,
id
}).then(res => {
if(res.data) {
const walk = (arr) => {
arr.forEach(item => {
if(item.childInfoVOList && !item.childInfoVOList.length) {
delete item.childInfoVOList
}else {
walk(item.childInfoVOList)
}
})
}
walk([res.data])
this.treeData = [res.data];
}
})
},
exportTableData() {
if(!this.searchObj.timevalue) {
this.$message.warning('请选择时间范围')
return
}
let startTime = ''
let endTime = ''
if(this.searchObj.timevalue[0] && isNaN(this.searchObj.timevalue[0])) {
startTime = this.$moment(this.searchObj.timevalue[0].getTime()).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.searchObj.timevalue[1].getTime()).format('YYYY-MM-DD HH:mm:ss')
} else {
startTime = this.$moment(this.searchObj.timevalue[0]).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.searchObj.timevalue[1]).format('YYYY-MM-DD HH:mm:ss')
}
const tenantVOS = []
const areaCodes = this.searchObj.treeIds
const walk = (arr) => {
arr.forEach(item => {
if(areaCodes.includes(item.id)) {
tenantVOS.push({
'id': item.id, 'type': item.type
})
}
if(item.childInfoVOList) {
walk(item.childInfoVOList)
}
})
}
if(areaCodes && areaCodes.length > 0) {
walk(this.treeData)
}
const params = {
'pageNo': 1,
'pageSize': 2000,
'quality': this.searchObj.quality,
'periodStartTime': startTime,
'periodEndTime': endTime,
'tenantVOS': tenantVOS,
'programName': this.searchObj.programName
}
if(!this.searchObj.quality) {
delete params.quality
}
Axios.post(
api.getDustMonitorOverviewExport(
this.globalTenantId,
this.type,
this.id
), params, {
responseType: 'blob'
}
)
.then(res => {
const content = res.data;
const blob = new Blob([content], { type: 'application/ms-excel' });
let fileName = res.headers['content-disposition'].split('=')[1];
/* let Base64 = require('js-base64').Base64;
fileName = Base64.decode(fileName)*/
fileName = '扬尘监测统计列表.xlsx' || decodeURIComponent(fileName);
if ('download' in document.createElement('a')) {
// 非IE下载
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
} else {
// IE10+下载
navigator.msSaveBlob(blob, fileName);
}
})
.catch(mes => {
console.log(mes)
})
// downloadExcle(api.getDustMonitorOverviewExport(this.globalTenantId, this.type, this.id), {
// ...params
// }, '超标项目统计', this.getDustMonitorOverview(this.type, this.id))
},
getDataMy() {
if(this.pageNo * this.pageSize > this.totalCount) {
return
}
this.pageNo++
this.getDustMonitorOverview()
},
handleClickDialog(val) {
this.dialogTitle = val.programName
this.dialogObj = val
this.detailObj = this.$options.data().detailObj
this.getDustMonitorOverviewDetail(val)
this.$refs.myDialog.dialogVisible = true
},
fetchTableData() {
this.pageNo = 1
this.getDustMonitorOverview()
},
getDustMonitorOverview() {
if(!this.searchObj.timevalue) {
this.$message.warning('请选择时间范围')
return
}
let startTime = ''
let endTime = ''
if(this.searchObj.timevalue[0] && isNaN(this.searchObj.timevalue[0])) {
startTime = this.$moment(this.searchObj.timevalue[0].getTime()).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.searchObj.timevalue[1].getTime()).format('YYYY-MM-DD HH:mm:ss')
} else {
startTime = this.$moment(this.searchObj.timevalue[0]).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.searchObj.timevalue[1]).format('YYYY-MM-DD HH:mm:ss')
}
const tenantVOS = []
const areaCodes = this.searchObj.treeIds
const walk = (arr) => {
arr.forEach(item => {
if(areaCodes.includes(item.id)) {
tenantVOS.push({
'id': item.id, 'type': item.type
})
}
if(item.childInfoVOList) {
walk(item.childInfoVOList)
}
})
}
if(areaCodes && areaCodes.length > 0) {
walk(this.treeData)
}
const params = {
'pageNo': this.pageNo,
'pageSize': this.pageSize,
'quality': this.searchObj.quality,
'periodStartTime': startTime,
'periodEndTime': endTime,
'tenantVOS': tenantVOS,
'programName': this.searchObj.programName
}
if(!this.searchObj.quality) {
delete params.quality
}
this.loading = true
this.$post(
api.getDustMonitorOverview(
this.globalTenantId,
this.type,
this.id
), params
)
.then(res => {
this.totalCount = res.data.totalCount
if(this.pageNo === 1) {
this.loading = false
this.items = res.data.records
} else {
this.loading = false
this.items = this.items.concat(res.data.records)
}
})
.catch(mes => {
this.loading = false
console.log(mes)
})
},
fetchTableDataDetail() {
if(!this.detailObj.timevalue[0]) {
this.$message.warning('请选择时间区域')
return
}
let startTime = ''
let endTime = ''
if(this.detailObj.timevalue[0] && isNaN(this.detailObj.timevalue[0])) {
startTime = this.$moment(this.detailObj.timevalue[0].getTime()).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.detailObj.timevalue[1].getTime()).format('YYYY-MM-DD HH:mm:ss')
} else {
startTime = this.$moment(this.detailObj.timevalue[0]).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.detailObj.timevalue[1]).format('YYYY-MM-DD HH:mm:ss')
}
const params = {
'pageNo': 1,
'pageSize': 10,
'quality': this.qualityObj[this.dialogObj.airQuality],
'periodStartTime': startTime,
'periodEndTime': endTime,
'programId': this.dialogObj.programId,
'programName': this.dialogObj.programName
}
this.$post(
api.getDustMonitorOverviewDetail(
this.globalTenantId,
this.type,
this.id
), params
)
.then(res => {
if(res.data) {
const xdata = []
const series = []
res.data.forEach(item => {
xdata.push(item.statisticsTime)
series.push(item.pm10)
})
this.options.xAxis.data = xdata;
this.options.series[0].data = series;
}
})
.catch(mes => {
console.log(mes)
})
},
getDustMonitorOverviewDetail(val) {
let startTime = ''
let endTime = ''
if(!this.detailObj.timevalue[0]) {
this.$message.warning('请选择时间区域')
return
}
if(this.detailObj.timevalue[0] && isNaN(this.detailObj.timevalue[0])) {
startTime = this.$moment(this.detailObj.timevalue[0].getTime()).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.detailObj.timevalue[1].getTime()).format('YYYY-MM-DD HH:mm:ss')
} else {
startTime = this.$moment(this.detailObj.timevalue[0]).format('YYYY-MM-DD HH:mm:ss')
endTime = this.$moment(this.detailObj.timevalue[1]).format('YYYY-MM-DD HH:mm:ss')
}
const params = {
'pageNo': 1,
'pageSize': 10,
'quality': this.qualityObj[this.dialogObj.airQuality],
'periodStartTime': startTime,
'periodEndTime': endTime,
'programId': this.dialogObj.programId,
'programName': this.dialogObj.programName
}
this.$post(
api.getDustMonitorOverviewDetail(
this.globalTenantId,
this.type,
this.id
), params
)
.then(res => {
const xdata = []
const series = []
const series1 = []
res.data.forEach(item => {
xdata.push(item.statisticsTime)
series.push(item.pm10)
series1.push(item.pm25)
})
this.options.xAxis.data = xdata;
this.options.series[0].data = series;
this.options.series[1].data = series1;
})
.catch(mes => {
console.log(mes)
})
},
resetClick() {
this.searchObj = this.$options.data().searchObj
this.getDustMonitorOverview()
},
setOptions(series) {
this.options.xAxis.data = this.xdata;
this.options.series[0].data = series;
// this.options.dataZoom = this.xdata.length > 50 ? [
// {
// startValue: 0,
// endValue: 8,
// type: 'slider',
// show: true,
// xAxisIndex: [0],
// handleSize: 0,
// height: 8,
// left: '4%',
// right: '2%',
// bottom: 0,
// borderColor: '#042e2c',
// fillerColor: 'rgba(64, 158, 255, 0.5)',
// borderRadius: 10,
// backgroundColor: '#04152e',
// showDataShadow: false,
// showDetail: false,
// realtime: true,
// filterMode: 'filter'
// }
// ] : []
}
}
}
</script>
<style scoped lang="less">
.legend-wrap{
// width: 300px;
align-items: center;
display: flex;
margin-left: 10px;
flex-wrap: wrap;
dl{
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-end;
dd{
margin-left: 5px;
margin-bottom: 5px;
white-space: nowrap;
b{
margin-left: 2px;
display: inline-block;
width: 10px;
height: 10px;
border-radius: 2px;
}
}
}
}
.table-wrap{
height: 100%;
overflow: auto;
padding-bottom: 80px;
}
.mr20{
margin-right: 20px;
}
.mycharts2{
width: 100%;
}
.level-class{
background-color: rgba(138, 12, 16,.4);
}
.level-class1{
background-color: rgb(206, 17, 24,.4);
}
.level-class2{
background-color: rgba(244, 102, 73,.4);
}
.level-class3{
background-color: rgb(246, 144, 61,.4);
}
.level-class4{
background-color: rgba(246, 189, 22,.4);
}
.level-class5{
background-color: rgba(97, 221, 170,.4);
}
.level-class6{
background-color: rgb(51, 153, 255,.4);
}
.level-class7{
background-color: rgb(101, 120, 155,.4);
}
.recycle-scroller-demo:not(.page-mode) {
height: 100%;
}
.recycle-scroller-demo.page-mode:not(.full-page) {
height: 100%;
}
.recycle-scroller-demo.page-mode {
flex: auto 0 0;
}
.recycle-scroller-demo.page-mode .toolbar {
border-bottom: solid 1px #e0edfa;
}
.content {
height: 100%;
flex: 100% 1 1;
position: relative;
}
.recycle-scroller-demo.page-mode:not(.full-page) .content {
overflow: auto;
}
.recycle-scroller-demo:not(.page-mode) .wrapper {
overflow: hidden;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.scroller {
width: 100%;
height: 100%;
}
.container{
// overflow: auto;
height: 100%;
}
.content-wrap{
overflow: auto;
height: 100%;
}
/deep/ .vue-recycle-scroller__item-view{
padding: 20px 10px;
border-bottom: 1px solid #3399FF;
border-left: 1px solid #3399FF;
border-right: 1px solid #3399FF;
}
.container{
.table-head{
display: flex;
flex-direction: row;
justify-content: flex-start;
border: 1px solid #3399FF;
background: rgba(51, 153, 255,0.3);
dt{
padding: 10px 10px;
width: 15%;
color: #fff;
opacity: 1;
}
}
.table-body{
display: flex;
flex-direction: row;
justify-content: flex-start;
padding: 20px 10px;
border-bottom: 1px solid #3399FF;
border-left: 1px solid #3399FF;
border-right: 1px solid #3399FF;
dd{
width: 15%;
}
}
}
.header-search-wrap{
display: flex;
flex-direction: row;
justify-content: flex-start;
padding: 15px 0;
.el-input{
width: 200px;
}
/deep/ .el-input__inner{
height: 40px;
line-height: 40px;
border: 1px solid rgba(16, 220, 250, 0.4);
background-color: transparent;
color: #fff;
}
/deep/ .el-input__inner{
background-color: transparent;
border: 1px solid rgba(16, 220, 250, 0.4);
color: #fff;
}
/deep/ .el-input__inner:focus{
opacity: 1;
}
.el-select{
vertical-align: top;
/deep/ .el-input{
width: 160px;
.el-input__inner{
height: 36px;
line-height: 36px;
background-color: transparent;
color: #fff;
border: 1px solid rgba(16, 220, 250, 0.4);
}
}
}
.el-cascader{
/deep/.el-input__inner{
border: 1px solid rgba(16, 220, 250, 0.4);
background-color: transparent;
height: 36px;
}
/deep/.el-input__inner:hover{
border: 1px solid rgba(16, 220, 250, 0.4);
background-color: transparent;
}
/deep/.el-input__inner:focus{
border: 1px solid rgba(16, 220, 250, 0.4);
background-color: transparent;
}
}
/deep/ .el-date-editor .el-range-input{
color: #fff;
}
/deep/ .el-date-editor .el-range-separator{
color: #fff;
}
.el-button--mini, .el-button--mini.is-round{
padding: 11px 12px;
}
.el-button--primary{
background: rgba(5, 35, 76, 0.2);
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.4);
}
.el-button--primary:hover{
color: #10DCFA;
border: 1px solid #10DCFA;
}
.el-button+.el-button{
margin-left: 6px;
}
}
</style>