完成电量分析部分图表

This commit is contained in:
re-JZzzz
2025-09-20 20:03:46 +08:00
parent 7eabcd203f
commit 55f2aeea39
4 changed files with 583 additions and 0 deletions

View File

@ -0,0 +1,225 @@
<template>
<div class="detaildata-container">
<el-table
v-loading="loading"
:data="tableData"
style="width: 100%"
stripe
border
>
<el-table-column prop="datetime" label="日期" align="center" />
<el-table-column prop="prbs" label="发电量(Kwh)" align="center">
<template #default="scope">
<span>{{ scope.row.prbs }}</span>
</template>
</el-table-column>
<el-table-column prop="prz" label="同比(%)" align="center">
<template #default="scope">
<span :class="{
'text-red': scope.row.prz < 0,
'text-green': scope.row.prz > 0
}">
{{ scope.row.prz > 0 ? '+' : '' }}{{ scope.row.prz }}
</span>
</template>
</el-table-column>
<el-table-column prop="prz2" label="环比(%)" align="center">
<template #default="scope">
<span :class="{
'text-red': scope.row.prz2 < 0,
'text-green': scope.row.prz2 > 0
}">
{{ scope.row.prz2 > 0 ? '+' : '' }}{{ scope.row.prz2 }}
</span>
</template>
</el-table-column>
<el-table-column prop="status" label="设备状态" align="center">
<template #default="scope">
<el-tag
:type="scope.row.status === '正常' ? 'success' : 'warning'"
size="small"
>
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template #default="scope">
<el-button
type="text"
size="small"
@click="handleDetail(scope.row)"
class="text-blue"
>
详情
</el-button>
<el-button
type="text"
size="small"
@click="handleExport(scope.row)"
class="text-blue"
>
导出
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
// 定义表格数据类型
interface TableRow {
datetime: string
prbs: string | number
prz: number
prz2: number
status: string
}
// 响应式数据
const loading = ref(false)
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(293)
const tableData = ref<TableRow[]>([])
// 模拟数据生成函数
const generateMockData = (page: number, size: number): TableRow[] => {
const data: TableRow[] = []
const startIndex = (page - 1) * size
// 生成不同的日期
const baseDate = new Date(2023, 5, 30)
for (let i = 0; i < size; i++) {
const index = startIndex + i
if (index >= total.value) break
// 生成不同的日期
const currentDate = new Date(baseDate)
currentDate.setDate(baseDate.getDate() - index)
const dateStr = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(currentDate.getDate()).padStart(2, '0')}`
// 随机生成正负数,展示不同颜色效果
const randomValue1 = (Math.random() - 0.5) * 10
const randomValue2 = (Math.random() - 0.5) * 10
data.push({
datetime: dateStr,
prbs: (Math.random() * 100 + 150).toFixed(1), // 150-250之间的随机数
prz: Number(randomValue1.toFixed(1)),
prz2: Number(randomValue2.toFixed(1)),
status: i % 8 === 2 ? '预警' : '正常'
})
}
return data
}
// 处理分页大小变化
const handleSizeChange = (size: number) => {
pageSize.value = size
loadData()
}
// 处理当前页码变化
const handleCurrentChange = (current: number) => {
currentPage.value = current
loadData()
}
// 处理详情按钮点击
const handleDetail = (row: TableRow) => {
ElMessage.info('查看详情: ' + row.datetime)
// 实际项目中这里应该跳转到详情页或显示详情对话框
}
// 处理导出按钮点击
const handleExport = (row: TableRow) => {
ElMessage.info('导出数据: ' + row.datetime)
// 实际项目中这里应该调用导出API
}
// 加载数据
const loadData = () => {
loading.value = true
// 模拟API请求延迟
setTimeout(() => {
tableData.value = generateMockData(currentPage.value, pageSize.value)
loading.value = false
}, 500)
}
// 组件挂载时加载数据
onMounted(() => {
loadData()
})
</script>
<style scoped lang="scss">
.detaildata-container {
padding: 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.pagination-container {
margin-top: 16px;
display: flex;
justify-content: flex-end;
align-items: center;
}
.text-red {
color: #f56c6c;
}
.text-green {
color: #67c23a;
}
.text-blue {
color: #1890ff;
}
.el-button--text {
padding: 0;
height: auto;
font-size: 14px;
}
// 响应式布局
@media screen and (max-width: 1200px) {
.detaildata-container {
padding: 12px;
}
.el-table {
font-size: 13px;
}
.el-table-column {
&:not(:first-child):not(:last-child) {
width: 90px !important;
}
}
}
</style>

View File

@ -0,0 +1,174 @@
<template>
<div class="duibifenxi-bar-container">
<div ref="chartRef" class="chart" style="width: 100%; height: 300px;"></div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
// 定义组件props
interface CompareData {
dates: string[]
currentPeriodData: number[]
lastYearData: number[]
}
const props = defineProps<{
compareData?: CompareData
}>()
const chartRef = ref<HTMLElement>()
let chartInstance: echarts.ECharts | null = null
// 默认数据
const defaultCompareData: CompareData = {
dates: ['1号', '2号', '3号', '4号', '5号', '6号', '7号'],
currentPeriodData: [90, 80, 75, 89, 60, 76, 73],
lastYearData: [60, 53, 65, 76, 69, 52, 65]
}
// 初始化图表
const initChart = () => {
if (!chartRef.value) return
chartInstance = echarts.init(chartRef.value)
const data = props.compareData || defaultCompareData
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function(params: any) {
const current = params[0]
const lastYear = params[1]
let result = `${current.name}<br/>`
result += `${current.marker}${current.seriesName}: ${current.value}Kwh<br/>`
result += `${lastYear.marker}${lastYear.seriesName}: ${lastYear.value}Kwh`
return result
}
},
legend: {
data: ['当前周期', '去年同期'],
textStyle: {
color: '#333'
},
top: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: data.dates,
axisLine: {
lineStyle: {
color: '#d9d9d9'
}
},
axisLabel: {
color: '#666'
}
},
yAxis: {
type: 'value',
name: 'kwh',
nameTextStyle: {
color: '#666',
padding: [0, 0, 0, 40]
},
axisLine: {
show: false
},
axisLabel: {
color: '#666'
},
splitLine: {
lineStyle: {
color: '#f0f0f0',
type: 'dashed'
}
}
},
series: [
{
name: '当前周期',
type: 'bar',
data: data.currentPeriodData,
itemStyle: {
color: '#1890ff'
},
barWidth: '30%',
emphasis: {
focus: 'series'
}
},
{
name: '去年同期',
type: 'bar',
data: data.lastYearData,
itemStyle: {
color: '#52c41a'
},
barWidth: '30%',
emphasis: {
focus: 'series'
}
}
]
}
chartInstance.setOption(option)
}
// 响应式处理
const handleResize = () => {
chartInstance?.resize()
}
// 组件挂载
onMounted(() => {
initChart()
window.addEventListener('resize', handleResize)
})
// 组件卸载
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
chartInstance?.dispose()
})
</script>
<style scoped lang="scss">
.duibifenxi-bar-container {
padding: 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
height: 100%;
display: flex;
flex-direction: column;
}
.chart {
flex: 1;
min-height: 0;
}
// 响应式调整
@media screen and (max-width: 768px) {
.duibifenxi-bar-container {
padding: 12px;
}
.chart {
height: 250px;
}
}
</style>

View File

@ -0,0 +1,172 @@
<template>
<div class="tongbifenxi-line-container">
<div id="tongbifenxiLineChart" class="chart-container"></div>
</div>
</template>
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue';
import * as echarts from 'echarts';
const chartInstance = ref<echarts.ECharts | null>(null);
const initChart = () => {
const chartDom = document.getElementById('tongbifenxiLineChart');
if (!chartDom) return;
chartInstance.value = echarts.init(chartDom);
// 写死的数据
const dates = ['1号', '2号', '3号', '4号', '5号', '6号', '7号'];
const growthRates = ['1.50', '1.20', '0.50', '0.80', '0.90', '0.30', '-2.00'];
const option: echarts.EChartsOption = {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
borderColor: '#409eff',
textStyle: {
color: '#fff'
},
formatter: (params: any) => {
const data = params[0];
return `${data.name}:\n环比增长率: ${data.value}%`;
},
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: dates,
axisTick: {
alignWithLabel: true
},
axisLine: {
lineStyle: {
color: '#d9d9d9'
}
},
axisLabel: {
color: '#666'
}
}
],
yAxis: [
{
type: 'value',
min: -2,
max: 2,
axisLabel: {
color: '#666',
formatter: '{value}%'
},
axisLine: {
show: true,
lineStyle: {
color: '#d9d9d9'
}
},
splitLine: {
lineStyle: {
color: '#f0f0f0',
type: 'dashed'
}
}
}
],
series: [
{
name: '环比增长率',
type: 'line',
stack: 'Total',
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(103, 194, 58, 0.3)'
},
{
offset: 1,
color: 'rgba(103, 194, 58, 0.05)'
}
])
},
emphasis: {
focus: 'series'
},
lineStyle: {
color: '#67c23a',
width: 3
},
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#67c23a',
borderColor: '#fff',
borderWidth: 2
},
data: growthRates,
smooth: true
}
]
};
chartInstance.value.setOption(option);
};
const handleResize = () => {
chartInstance.value?.resize();
};
onMounted(() => {
initChart();
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
chartInstance.value?.dispose();
});
</script>
<style scoped>
.tongbifenxi-line-container {
width: 100%;
height: 100%;
min-height: 300px;
padding: 10px;
box-sizing: border-box;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.chart-container {
width: 100%;
height: 100%;
min-height: 280px;
}
@media (max-width: 768px) {
.tongbifenxi-line-container {
padding: 5px;
min-height: 250px;
}
.chart-container {
min-height: 230px;
}
}
</style>

View File

@ -0,0 +1,12 @@
<template>
<div>
<DuibifenxiBar></DuibifenxiBar>
<tongbifenxiLine></tongbifenxiLine>
<detaildata></detaildata>
</div>
</template>
<script setup>
import detaildata from '@/views/shengchanManage/powerfenxi/components/detaildata.vue'
import tongbifenxiLine from './components/tongbifenxiLine.vue';
import DuibifenxiBar from './components/duibifenxiBar.vue';
</script>