@@ -1,516 +1,203 @@
< script setup lang = "ts" >
import { RadioGroup , RadioButton , message } from 'ant-design-vue' ;
import { Page } from '@vben/common-ui' ;
import { BackTop } from 'ant-design-vue' ;
import { ref , onMounted , onBeforeUnmount , reactive } from 'vue' ;
import * as echarts from 'echarts' ;
import type { ECharts , EChartsOption } from 'echarts' ;
import FloorTree from '../components/floor-tree.vue' ;
import dayjs from 'dayjs' ;
import { meterRecordTrend } from '#/api/property/energyManagement/meterRecord' ;
// 左边楼层用
const selectFloorId = ref < string [ ] > ( [ ] ) ;
onMounted ( ( ) => { } ) ;
const chainData = reactive ( {
todayEnergy : '231.78' ,
yesterdaySamePeriodEnergy : '269.56' ,
dayTrendPercentage : '-14.02%' ,
dayTrendValue : '-37.78' ,
currentMonthEnergy : '18758.39' ,
lastMonthSamePeriodEnergy : '--' ,
monthTrendPercentage : '--' ,
monthTrendValue : '--' ,
currentYearEnergy : '18758.39' ,
lastYearSamePeriodEnergy : '--' ,
yearTrendPercentage : '--' ,
yearTrendValue : '--' ,
} ) ;
const peakData = reactive ( {
todayPeakPower : '2961.08' ,
todayPeakTime : dayjs ( ) . format ( 'YYYY-MM-DD HH:mm:ss' ) ,
yesterdayPeakPower : '2993.89' ,
yesterdayPeakTime : dayjs ( ) . subtract ( 1 , 'day' ) . format ( 'YYYY-MM-DD HH:mm:ss' ) ,
} ) ;
const energyTrendTime = ref ( '1' ) ;
// 能耗趋势图表容器
const energyTrendChart = ref < HTMLElement | null > ( null ) ;
const energyTrendInstance = ref < ECharts | null > ( null ) ;
const energyTrendOption : EChartsOption = {
tooltip : {
trigger : 'item' ,
axisPointer : {
type : 'shadow' ,
} ,
} ,
xAxis : {
type : 'category' ,
data : [ ] ,
name : '时间' ,
} ,
yAxis : {
type : 'value' ,
name : 'kW.h' ,
} ,
series : [
{
data : [ ] ,
type : 'bar' ,
markPoint : {
data : [
{ type : 'max' , name : 'Max' } ,
{ type : 'min' , name : 'Min' } ,
] ,
} ,
} ,
] ,
} ;
const initEnergyTrendChart = ( ) => {
if ( energyTrendChart . value ) {
// 销毁旧实例
energyTrendInstance . value ? . dispose ( ) ;
// 创建新实例
energyTrendInstance . value = echarts . init ( energyTrendChart . value ) ;
// 设置配置项
energyTrendInstance . value . setOption ( energyTrendOption ) ;
// buildingEnergyTrendData('1');
// 可选:添加窗口大小变化监听
const resizeHandler = ( ) => {
energyTrendInstance . value ? . resize ( ) ;
} ;
window . addEventListener ( 'resize' , resizeHandler ) ;
// 在组件卸载前移除监听
onBeforeUnmount ( ( ) => {
window . removeEventListener ( 'resize' , resizeHandler ) ;
} ) ;
}
} ;
function buildingEnergyTrendData ( val : string ) {
if ( trendData . value . hour == null ) {
message . warning ( '请先选择楼层或电表!' ) ;
return ;
}
const now = new Date ( ) ;
let timeArr = [ ] ;
let valArr = [ ] ;
let name = '时间' ;
if ( val == '1' ) {
const hour = now . getHours ( ) ;
for ( let i = 0 ; i < hour ; i ++ ) {
timeArr . push ( i + ':00' ) ;
}
valArr = trendData . value . hour . today . data ;
} else if ( val == '2' ) {
const day = now . getDate ( ) ;
for ( let i = 1 ; i < day ; i ++ ) {
timeArr . push ( i ) ;
}
name = '日期' ;
valArr = trendData . value . day . nowMonth . data ;
} else {
const month = now . getMonth ( ) + 1 ;
for ( let i = 1 ; i < month + 1 ; i ++ ) {
timeArr . push ( i ) ;
}
name = '月份' ;
valArr = trendData . value . month . nowYear . data ;
}
if ( energyTrendInstance . value ) {
energyTrendInstance . value . setOption ( {
xAxis : { data : timeArr , name } ,
series : [ { data : valArr } ] ,
} ) ;
}
}
//日用电率
const powerCurveChart = ref < HTMLElement | null > ( null ) ;
const powerCurveInstance = ref < ECharts | null > ( null ) ;
const powerCurveOption : EChartsOption = {
tooltip : {
trigger : 'item' ,
axisPointer : {
type : 'cross' ,
crossStyle : {
color : '#999' ,
} ,
} ,
} ,
toolbox : {
feature : {
magicType : { show : true , type : [ 'line' , 'bar' ] } ,
restore : { show : true } ,
} ,
} ,
legend : {
data : [ '今日' , '昨日' ] ,
} ,
xAxis : [
{
type : 'category' ,
data : [
'0:00' ,
'1:00' ,
'2:00' ,
'3:00' ,
'4:00' ,
'5:00' ,
'6:00' ,
'7:00' ,
'8:00' ,
'9:00' ,
'10:00' ,
'11:00' ,
'12:00' ,
'13:00' ,
'14:00' ,
'15:00' ,
'16:00' ,
'17:00' ,
'18:00' ,
'19:00' ,
'20:00' ,
'21:00' ,
'22:00' ,
'23:00' ,
] ,
axisPointer : {
type : 'shadow' ,
} ,
name : '时' ,
} ,
] ,
yAxis : [
{
type : 'value' ,
name : 'kW' ,
} ,
] ,
series : [
{
name : '昨日' ,
type : 'line' ,
smooth : true ,
data : [ ] ,
markPoint : {
data : [ { type : 'max' } , { type : 'min' } ] ,
} ,
itemStyle : { color : '#cbb0e3' } , // 数据点颜色
lineStyle : { color : '#cbb0e3' } , // 线条颜色
} ,
] ,
} ;
const initPowerCurveChart = ( ) => {
if ( powerCurveChart . value ) {
// 销毁旧实例
powerCurveInstance . value ? . dispose ( ) ;
// 创建新实例
powerCurveInstance . value = echarts . init ( powerCurveChart . value ) ;
// 设置配置项
powerCurveInstance . value . setOption ( powerCurveOption ) ;
// 可选:添加窗口大小变化监听
const resizeHandler = ( ) => {
powerCurveInstance . value ? . resize ( ) ;
} ;
window . addEventListener ( 'resize' , resizeHandler ) ;
// 在组件卸载前移除监听
onBeforeUnmount ( ( ) => {
window . removeEventListener ( 'resize' , resizeHandler ) ;
} ) ;
}
} ;
// 组件挂载后初始化图表
onMounted ( ( ) => {
initEnergyTrendChart ( ) ;
initPowerCurveChart ( ) ;
} ) ;
// 组件卸载前销毁图表实例
onBeforeUnmount ( ( ) => {
energyTrendInstance . value ? . dispose ( ) ;
powerCurveInstance . value ? . dispose ( ) ;
} ) ;
onBeforeUnmount ( ( ) => { } ) ;
const trendData = ref < any > ( { } ) ;
async function handleSelectFloor ( selectedKeys , info ) {
const now = new Date ( ) ;
// 获取年、月、日
const year = now . getFullYear ( ) ;
// 月份从0开始, 所以要+1, 并格式化为两位数
const month = String ( now . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
// 日期格式化为两位数
const day = String ( now . getDate ( ) ) . padStart ( 2 , '0' ) ;
async function handleSelectFloor ( selectedKeys , info ) { }
let data = {
day : year + '-' + month + '-' + day ,
month : year + '-' + month ,
year : year ,
meterType : 1 ,
meterId : null ,
floorId : null ,
} ;
if ( info . node . level == 3 ) {
data . floorId = selectedKeys [ 0 ] ;
} else {
data . meterId = selectedKeys [ 0 ] ;
}
const trend = await meterRecordTrend ( data ) ;
trendData . value = trend ;
// 趋势曲线
let timeArr = [ ] ;
let valArr = [ ] ;
let name = '时间' ;
if ( energyTrendTime . value == '1' ) {
const hour = now . getHours ( ) ;
for ( let i = 0 ; i < hour ; i ++ ) {
timeArr . push ( i + ':00' ) ;
}
valArr = trend . hour . today . data ;
} else if ( energyTrendTime . value == '2' ) {
const day = now . getDate ( ) ;
for ( let i = 1 ; i < day ; i ++ ) {
timeArr . push ( i ) ;
}
name = '日期' ;
valArr = trend . day . nowMonth . data ;
} else {
const month = now . getMonth ( ) + 1 ;
for ( let i = 1 ; i < month + 1 ; i ++ ) {
timeArr . push ( i ) ;
}
name = '月份' ;
valArr = trend . month . nowYear . data ;
}
if ( energyTrendInstance . value ) {
energyTrendInstance . value . setOption ( {
xAxis : { data : timeArr , name } ,
series : [ { data : valArr } ] ,
} ) ;
}
if ( powerCurveInstance . value ) {
powerCurveInstance . value . setOption ( {
series : [
{
name : '今日' ,
type : 'line' ,
smooth : true ,
data : trend . hour . today . data ,
markPoint : {
data : [ { type : 'max' } , { type : 'min' } ] ,
} ,
} ,
{
name : '昨日' ,
type : 'line' ,
smooth : true ,
data : trend . hour . yesterday . data ,
markPoint : {
data : [ { type : 'max' } , { type : 'min' } ] ,
} ,
} ,
] ,
} ) ;
}
}
function targetFn ( ) {
return document . querySelector ( '.flex-1' )
}
< / script >
< template >
< Page :auto-content-height = "true" >
< div class = "flex h-full gap-[8px]" >
< FloorTree class = "w-[260px]" @select ="handleSelectFloor" > < / FloorTree >
< div class = "flex-1 overflow-hidden " >
< div class = "row " >
< div class = "energy-trend-container" >
< div class = "energy-trend-top" >
< div class = "section-header " >
< div class = "header-title" > 能耗趋势 < / div >
< / div >
< RadioGroup
v -model :value = "energyTrendTime"
button -style = " solid "
size = "small"
@change ="buildingEnergyTrendData(energyTrendTime)"
>
< RadioButton value = "1" > 当日 < / RadioButton >
< RadioButton value = "2" > 当月 < / RadioButton >
< RadioButton value = "3" > 当年 < / RadioButton >
< / RadioGroup >
< div class = "flex-1" >
< div class = "cards-container " >
< div v-for = "item in 50" class="plan-card" >
< h2 > 4 楼电表1号 < span > For business sedrvices < / span > < / h2 >
< div class = "etiquet-price " >
< p > 254.99 < / p >
< div > < / div >
< / div >
< div class = "b enefits-list" >
< ul >
< li > < span > Anlysis < / span > < / li >
< li > < span > Consulting < / span > < / li >
< li > < span > Consulting < / span > < / li >
< / ul >
< / div >
< div class = "button-get-plan" >
< a href = "#" >
< span > START PROJECT < / span >
< / a >
< / div >
< div class = "chart-placeholder" ref = "energyTrendChart" > < / div >
< / div >
< / div >
< div class = "row" >
< div class = "power-curve-container" >
< div class = "section-header" >
< div class = "header-title" > 平均电功率曲线 < / div >
< / div >
< div class = "power-chart" ref = "powerCurveChart" > < / div >
< / div >
<!-- < div class = "power-peak-container" >
< div class = "section-header" >
< div class = "header-title" > 电功率峰值 < / div >
< / div >
< div class = "peak-item" >
< p class = "value" > { { peakData . todayPeakPower } } < / p >
< p class = "time" > { { peakData . todayPeakTime } } < / p >
< div class = "bottom-text" > 当日 ( kW ) < / div >
< / div >
< div class = "peak-item" >
< p class = "value" > { { peakData . yesterdayPeakPower } } < / p >
< p class = "time" > { { peakData . yesterdayPeakTime } } < / p >
< div class = "bottom-text" > 昨日 ( kW ) < / div >
< / div >
< / div > -- >
< / div >
< BackTop :target = "targetFn" > < / BackTop >
< / div >
< / div >
< / Page >
< / template >
< style scoped >
. row {
display : flex ;
gap : 1 rem ;
width : 100 % ;
}
. comparison - section - container {
flex : 2 ;
}
. energy - trend - container {
flex : 3 ;
}
. power - curve - container {
margin - top : 10 px ;
flex : 5 ;
}
. power - peak - container {
margin - top : 10 px ;
/* 右侧内容区域样式 */
. flex - 1 {
flex : 1 ;
overflow - y : auto ; /* 添加垂直滚动条 */
padding : 10 px ;
height : 100 % ; /* 确保高度占满父容器 */
box - sizing : border - box ; /* 包括padding在高度计算中 */
}
. energy - trend - top {
/* 卡片容器样式 */
. cards - container {
display : flex ;
justify - content : space - between ;
flex - wrap : wrap ;
justify - content : flex - start ;
gap : 20 px ; /* 使用gap替代margin控制间距 */
}
. section - header {
border - left : 4 px solid # 3671 e8 ;
margin - bottom : 15 px ;
padding - left : 16 px ;
}
. header - title {
font - size : 16 px ;
color : # 333 ;
}
. comparison - section - container ,
. energy - trend - container ,
. power - curve - container ,
. power - peak - container {
. plan - card {
background : # fff ;
width : 15 rem ;
padding - left : 2 rem ;
padding - right : 2 rem ;
padding - top : 10 px ;
padding - bottom : 20 px ;
border - radius : 10 px ;
border - bottom : 4 px solid # 87 ceeb ;
box - shadow : 0 6 px 30 px rgba ( 173 , 216 , 230 , 0.3 ) ;
font - family : 'Poppins' , sans - serif ;
height : 306 px ;
margin : 0 5 px 20 px 5 px ;
}
. plan - card h2 {
margin - bottom : 15 px ;
font - size : 27 px ;
color : # 5 faee3 ;
font - weight : 600 ;
}
. plan - card h2 span {
display : block ;
margin - top : - 4 px ;
color : # 7 eb8da ;
font - size : 12 px ;
font - weight : 400 ;
}
. etiquet - price {
position : relative ;
background : # f0f8ff ;
width : 14.46 rem ;
margin - left : - 0.65 rem ;
padding : 0.2 rem 1.2 rem ;
border - radius : 5 px 0 0 5 px ;
}
. etiquet - price p {
margin : 0 ;
padding - top : 0.4 rem ;
display : flex ;
font - size : 1.9 rem ;
font - weight : 500 ;
color : # 4 a8cbb ;
}
. etiquet - price p : before {
content : '' ;
margin - right : 5 px ;
font - size : 15 px ;
font - weight : 300 ;
}
. etiquet - price p : after {
content : '/ KW.H' ;
margin - left : 5 px ;
font - size : 15 px ;
font - weight : 300 ;
}
. etiquet - price div {
position : absolute ;
bottom : - 23 px ;
right : 0 px ;
width : 0 ;
height : 0 ;
border - top : 13 px solid # 87 ceeb ;
border - bottom : 10 px solid transparent ;
border - right : 13 px solid transparent ;
z - index : 1 ;
}
. benefits - list {
margin - top : 16 px ;
}
. benefits - list ul {
padding : 0 ;
font - size : 14 px ;
}
. benefits - list ul li {
color : # 5 a7d9a ;
list - style : none ;
margin - bottom : 0.2 rem ;
display : flex ;
align - items : center ;
gap : 0.5 rem ;
}
. benefits - list ul li svg {
width : 0.9 rem ;
fill : currentColor ;
}
. benefits - list ul li span {
font - weight : 300 ;
}
. button - get - plan {
display : flex ;
justify - content : center ;
margin - top : 1.2 rem ;
}
. button - get - plan a {
display : flex ;
justify - content : center ;
align - items : center ;
background : # 6 bb1e3 ;
color : # fff ;
padding : 10 px 15 px ;
border - radius : 5 px ;
box - shadow : 0 2 px 8 px rgba ( 0 , 0 , 0 , 0.1 ) ;
padding : 1 rem ;
text - decoration : none ;
font - size : 0.8 rem ;
letter - spacing : 0.05 rem ;
font - weight : 500 ;
transition : all 0.3 s ease ;
}
. comparison - grid {
display : grid ;
grid - template - columns : repeat ( 3 , 1fr ) ;
gap : 5 px ;
. button - get - plan a : hover {
transform : translateY ( - 3 % ) ;
box - shadow : 0 3 px 10 px rgba ( 12 3, 180 , 220 , 0.6 ) ; /* 浅蓝色阴影 */
background : # 5 fa0d0 ; /* 悬停时略深的蓝色 */
}
. comparison - item {
padding : 5 px 10 px ;
border : 1 px solid # e0e0e0 ;
text - align : cente r;
}
. item - value {
font - size : 22 px ;
color : # 333 ;
margin - bottom : 10 px ;
}
. item - title {
font - size : 12 px ;
color : # 666 ;
}
. item - top {
font - size : 16 px ;
margin - bottom : 10 px ;
}
. item - percent {
color : # 7 fb926 ;
margin - bottom : 5 px ;
border - bottom : 1 px solid # 666 ;
}
. item - unit {
font - size : 12 px ;
color : # 999 ;
margin - left : 3 px ;
}
. chart - placeholder {
height : 36 vh ;
display : flex ;
align - items : center ;
justify - content : center ;
border - radius : 4 px ;
}
. power - chart {
height : 38 vh ;
display : flex ;
align - items : center ;
justify - content : center ;
border - radius : 4 px ;
}
. peak - item {
padding : 15 px 0 0 0 ;
border : 1 px solid # 3671 e8 ;
text - align : center ;
margin - bottom : 1 rem ;
. bottom - text {
background - color : # 3671 e8 ;
color : white ;
line - height : 40 px ;
}
}
. peak - item . value {
font - size : 22 px ;
color : # 333 ;
}
. peak - item . time {
font - size : 12 px ;
color : # 999 ;
margin : 5 px 0 ;
. button - get - plan . svg - rocket {
margin - right : 10 px ;
width : 0.9 rem ;
fill : currentColo r;
}
< / style >