物管理前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

567 lines
15 KiB

<template>
<div class="borads-index">
<screen-adapter class="adapter">
<div class="boards-container">
<div class="center" title="使用鼠标左键、滚轮实现拖拽、放大缩小。"></div>
<div class="top">
<div class="title" @click="reset" title="点击此标题将鸟瞰图恢复成初始状态。">{{ $store.state.user.tenantName || '平台看板' }}</div>
<div class="time" v-if="timeCount && timeCount.ts > 0">
<div class="time-left">
{{ $m(timeCount.ts).format('HH:mm:ss') }}
</div>
<div class="time-right">
<div>{{ $date.getDay(timeCount.ts) }}</div>
<div>
{{ $m(timeCount.ts).format('YYYY/MM/DD') }}
</div>
</div>
</div>
</div>
<div class="pop">
<pop v-for="(item, index) in pop" :key="index" :item="item" :index="index"></pop>
</div>
<div class="left">
<block v-for="(item, index) in left" :key="index" :item="item" :data-form="dataForm" @change-code="dataForm.code = $event">
<component :is="item.comp" :key="index" :item="item" :option="item.option" tip-loop></component>
</block>
</div>
<div class="right">
<block v-for="(item, index) in right" :key="index" :item="item" :data-form="dataForm" @change-code="dataForm.code = $event">
<component :is="item.comp" :key="index" :item="item" :option="item.option" tip-loop></component>
</block>
</div>
<!--<div class="bottom">
<block size-type="2" :item="bottom">
<component :is="bottom.comp" :item="bottom" :option="bottom.option" tip-loop></component>
</block>
</div>-->
</div>
<tooltip :data="toolTipData" />
</screen-adapter>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, onMounted, nextTick, watch, onBeforeUnmount } from 'vue';
import ScreenAdapter from '@/components/screen-adapter.vue';
import Block from './comp/block.vue';
import Bar from './comp/bar.vue';
import Pop from './comp/pop.vue';
import timeCountHooks from '@/hooks/time-count';
import { useFetch } from '@/hooks/fetch';
import utilService from '@/service/utilService';
import Chart from '@/components/chart.vue';
import { costStatistic, elecPayload, monthEnergyUsageTrend, usageRanking, energyUsageDistribution } from './common/model';
import { IObject } from '@/types/interface';
import moment from 'moment';
import toolHooks from '@/hooks/tool';
import { init, setPointerLineStatusAll } from './common/style';
import Tooltip from './comp/tooltip.vue';
import switchScreenHooks from '@/hooks/switch-screen';
export default defineComponent({
components: {
ScreenAdapter,
Tooltip,
Block,
Chart,
Bar,
Pop,
},
setup() {
const { timeCount } = timeCountHooks();
const { generateSvg2Dom } = toolHooks();
const state = reactive({
opt: { scale: 0.8 },
dragStretch: null,
toolTipData: {},
timer: null,
dataForm: {
code: '电',
codeMap: { : 'kWh', : 't', 蒸汽: 't', 压缩空气: 'Nm³' },
},
timeCount,
pop: [
{
key: 'yearCarbon',
icon: '',
label: '年碳排放量',
value: '--',
unit: 'tCO₂',
toFixed: 2,
},
{
key: 'monthConsumptionValue',
icon: '',
label: '月综合能耗',
value: '--',
unit: 'tce',
},
{
key: 'yearConsumptionValue',
icon: '',
label: '年综合能耗',
value: '--',
unit: 'tce',
},
],
left: [
{
comp: 'bar',
title: '月用能统计',
data: [
{
key: 'electricDayValue',
icon: 'elec',
label: '电用量',
value: '',
unit: 'kWh',
},
{
key: 'waterDayValue',
icon: 'water',
label: '水用量',
value: '',
unit: 't',
},
{
key: 'steamDayValue',
icon: 'steam',
label: '蒸汽用量',
value: '',
unit: 't',
},
{
key: 'airDayValue',
icon: 'air',
label: '压缩空气用量',
value: '',
unit: 'Nm³',
},
{
key: 'gasValue',
icon: 'gas',
label: '天然气用量',
value: '',
unit: 'Nm³',
},
],
},
{
title: '年用能统计',
comp: 'bar',
data: [
{
key: 'electricDayValue',
icon: 'elec',
label: '电用量',
value: '',
unit: 'kWh',
},
{
key: 'waterDayValue',
icon: 'water',
label: '水用量',
value: '',
unit: 't',
},
{
key: 'steamDayValue',
icon: 'steam',
label: '蒸汽用量',
value: '',
unit: 't',
},
{
key: 'airDayValue',
icon: 'air',
label: '压缩空气用量',
value: '',
unit: 'Nm³',
},
{
key: 'gasValue',
icon: 'gas',
label: '天然气用量',
value: '',
unit: 'Nm³',
},
],
},
{
comp: 'chart',
title: '当日电力负荷',
option: elecPayload(),
unit: 'kWh',
},
],
right: [
{
comp: 'chart',
title: '能耗统计(月)',
option: costStatistic(),
unit: '',
},
{
comp: 'chart',
title: '用能排行(月)',
option: usageRanking(),
unit: 'kWh',
select: true,
},
{
title: '当月用能趋势',
comp: 'chart',
option: monthEnergyUsageTrend(),
unit: 'kWh',
select: true,
},
],
bottom: {
comp: 'chart',
title: '24小时用能分布情况',
option: energyUsageDistribution(),
},
}) as IObject;
const changeType = (type: string) => {
const c = state.dataForm.code;
const u = state.dataForm.codeMap[c];
state.left[2].unit = u;
state.right[1].unit = u;
//当月用能趋势
useFetch('/board/monthEnergy/analyse', {
data: {
month: moment().format('YYYY-MM'),
type,
},
cb: (res: any) => {
if (utilService.isValidObject(res) && utilService.isValidArray(res.currentMonthData)) {
const d = res.currentMonthData;
const c = state.dataForm.code;
const u = state.dataForm.codeMap[c];
state.right[2].option = monthEnergyUsageTrend(d, u);
}
},
});
//用能排行(月)
useFetch('/board/monthEnergy/ranking', {
data: {
day: moment().format('YYYY-MM'),
type,
},
cb: (res: any) => {
if (utilService.isValidArray(res)) {
const c = state.dataForm.code;
const u = state.dataForm.codeMap[c];
state.right[1].option = usageRanking(res, u);
}
},
});
};
const fn = () => {
//三个小气泡
useFetch('/board/carbonAndConsumption', {
cb: (res: any) => {
if (utilService.isValidObject(res)) {
for (const item of state.pop) {
item.value = res[item.key] ? (item.toFixed ? utilService.number2Fixed(res[item.key]) : res[item.key]) : '--';
}
}
},
});
//月用能统计
useFetch('/board/eachEnergy', {
cb: (res: any) => {
if (utilService.isValidObject(res)) {
for (const item of state.left[0].data) {
item.value = res[item.key] ?? '--';
}
}
},
});
//年用能统计
useFetch('/board/eachEnergyYear', {
cb: (res: any) => {
if (utilService.isValidObject(res)) {
for (const item of state.left[1].data) {
item.value = res[item.key] ?? '--';
}
}
},
});
//能耗费用统计
useFetch('/board/energyCost', {
data: {
day: moment().format('YYYY-MM-DD'),
},
cb: (res: any) => {
if (utilService.isValidArray(res)) {
state.right[0].option = costStatistic(res);
}
},
});
//当日电力负荷
useFetch('/board/qcDayLoad/analyse', {
data: {
day: moment().format('YYYY-MM-DD'),
},
cb: (res: any) => {
if (utilService.isValidObject(res) && utilService.isValidArray(res.currentMonthData)) {
const d = res.currentMonthData;
state.left[2].option = elecPayload(d);
}
},
});
};
//24小时用能分布情况
// useFetch('/board/dayEnergy/trend', {
// data: {
// //day: '2025-04-01',
// day: moment().subtract(1, 'days').format('YYYY-MM-DD'),
// },
// cb: (res: any) => {
// if (utilService.isValidObject(res)) {
// state.bottom.option = energyUsageDistribution(res);
// }
// },
// });
changeType(state.dataForm.code);
fn();
const reset = () => {
setPointerLineStatusAll('none');
state.dragStretch.reset();
};
const methods = { changeType, reset };
watch(
() => state.dataForm.code,
(newCode: string) => {
changeType(newCode);
},
);
const getSvg = () => {
const target = document.querySelector('.boards-container .center') as HTMLElement;
generateSvg2Dom('/cdn/normal_bg.svg', target, () => {
target.style.transform = 'scale(0.7)';
nextTick(() => {
init(state, target);
// document.getElementById("layer-d-animate").innerHTML = '<animate href="#path23264" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate><animate href="#path23264-3" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate>';
});
});
};
onMounted(() => {
getSvg();
state.timer = setInterval(() => {
changeType(state.dataForm.code);
fn();
}, 1 * 60 * 1000);
});
onBeforeUnmount(() => {
clearInterval(state.timer);
state.timer = null;
});
return { ...toRefs(state), ...methods, ...switchScreenHooks({ screen: '1' }) };
},
});
</script>
<!-- <style lang="scss">
.rr-view-ctx-card {
height: 100%;
.el-card__body {
position: relative;
height: 100%;
}
}
</style> -->
<style lang="less" scoped>
.borads-index {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: #000b31;
.adapter {
.boards-container {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
> .top {
position: relative;
z-index: 2;
height: 88px;
> .title {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
color: #e2edff;
letter-spacing: 6px;
background: url('@/assets/images/njhl/title.png') no-repeat center;
background-size: 100% 100%;
text-align: center;
font-weight: bold;
font-size: 40px;
cursor: pointer;
}
> .time {
position: absolute;
left: 0;
top: 3px;
display: flex;
align-items: center;
> .time-left {
position: relative;
font-size: 32px;
margin: 0 60px;
background: linear-gradient(to top, #fff 30%, #3e576f 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-family: 'Alibaba PuHuiTi Heavy'; //阿里巴巴 普惠体
&:after {
position: absolute;
content: '';
background: #3e576f;
right: -30px;
top: 50%;
width: 1px;
height: 40px;
transform: translate(-50%, -50%);
}
}
> .time-right {
color: rgba(#fff, 0.7);
}
}
}
> .center {
position: absolute;
left: 0;
top: 0;
width: 1920px;
height: 1080px;
cursor: move;
//background: url("@/assets/images/njhl/bg.png");
//transform: translate(-50%, -50%);
/* 初始阴影设置(透明) */
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: inherit;
//opacity: 0;
//transition: opacity 0.4s;
box-shadow: inset 0 0 80px 30px #000b31;
}
&:hover::before {
// opacity: 1;
}
}
.icon {
opacity: 0.4;
position: absolute;
top: 8px;
right: 120px;
z-index: 999;
::v-deep svg {
transition: all 80ms;
// opacity: 0.8;
//&:hover {
// width: 35px !important;
// height: 35px !important;
// opacity: 1;
//}
}
}
.pop {
position: absolute;
display: flex;
align-items: center;
justify-content: space-between;
height: 86px;
left: 50%;
top: 88px;
transform: translate(-50%);
> * {
margin: 0 40px;
background: rgba(#000b31, 0.7);
}
}
.left,
.right {
position: absolute;
top: 88px;
height: calc(100% - 108px);
display: flex;
flex-direction: column;
justify-content: space-between;
background: rgba(#000b31, 0.7);
z-index: 2;
}
.bottom {
> * {
background-color: rgba(#000b31, 0.7) !important;
}
}
.left {
//left: 0;
left: 16px;
}
.right {
//right: 0;
right: 16px;
}
.bottom {
position: absolute;
bottom: 20px;
width: 100%;
height: 318px;
left: 0;
> * {
margin: 0 auto;
}
}
}
}
}
</style>