<template>
|
<div class="app-container">
|
<!-- 搜索区域 -->
|
<div class="search_form">
|
<el-form :model="searchForm"
|
:inline="true">
|
<el-form-item label="统计维度:">
|
<el-radio-group v-model="statisticsType"
|
@change="handleTypeChange">
|
<el-radio-button label="day">按日统计</el-radio-button>
|
<el-radio-button label="month">按月统计</el-radio-button>
|
</el-radio-group>
|
</el-form-item>
|
<el-form-item label="能耗类型:">
|
<el-select v-model="searchForm.energyType"
|
placeholder="全部"
|
clearable
|
style="width: 140px;"
|
@change="handleQuery">
|
<el-option label="全部"
|
value="全部" />
|
<el-option label="水"
|
value="水" />
|
<el-option label="电"
|
value="电" />
|
<el-option label="气"
|
value="气" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="能耗用途:">
|
<el-select v-model="searchForm.energyPurpose"
|
placeholder="全部"
|
clearable
|
style="width: 140px;"
|
@change="handleQuery">
|
<el-option label="全部"
|
value="全部" />
|
<el-option label="生产"
|
value="生产" />
|
<el-option label="办公"
|
value="办公" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="时间范围:">
|
<el-date-picker v-if="statisticsType === 'day'"
|
v-model="searchForm.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
value-format="YYYY-MM-DD"
|
style="width: 240px;"
|
@change="handleQuery" />
|
<el-date-picker v-else
|
v-model="searchForm.monthRange"
|
type="monthrange"
|
range-separator="至"
|
start-placeholder="开始月份"
|
end-placeholder="结束月份"
|
value-format="YYYY-MM"
|
style="width: 240px;"
|
@change="handleQuery" />
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary"
|
@click="handleQuery">查询</el-button>
|
<el-button @click="handleReset">重置</el-button>
|
</el-form-item>
|
</el-form>
|
<div>
|
<el-button type="success"
|
@click="handleExport">导出报表</el-button>
|
</div>
|
</div>
|
<!-- 统计概览卡片 -->
|
<div class="statistics-overview">
|
<h2 class="section-header">
|
<el-icon class="header-icon">
|
<DataLine />
|
</el-icon>
|
能耗成本概览
|
</h2>
|
<el-row :gutter="20">
|
<el-col :span="6">
|
<div class="overview-card blue-card">
|
<div class="overview-icon blue-icon">
|
<el-icon>
|
<Money />
|
</el-icon>
|
</div>
|
<div class="overview-info">
|
<div class="overview-label">总能耗成本</div>
|
<div class="overview-value">¥{{ overview.totalCost }}</div>
|
</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="overview-card green-card">
|
<div class="overview-icon green-icon">
|
<el-icon>
|
<DataLine />
|
</el-icon>
|
</div>
|
<div class="overview-info">
|
<div class="overview-label">生产能耗成本</div>
|
<div class="overview-value">¥{{ overview.productionCost }}</div>
|
</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="overview-card purple-card">
|
<div class="overview-icon purple-icon">
|
<el-icon>
|
<TrendCharts />
|
</el-icon>
|
</div>
|
<div class="overview-info">
|
<div class="overview-label">办公能耗成本</div>
|
<div class="overview-value">¥{{ overview.officeCost }}</div>
|
</div>
|
</div>
|
</el-col>
|
<el-col :span="6">
|
<div class="overview-card gray-card">
|
<div class="overview-icon gray-icon">
|
<el-icon>
|
<Histogram />
|
</el-icon>
|
</div>
|
<div class="overview-info">
|
<div class="overview-label">平均能耗成本</div>
|
<div class="overview-value">¥{{ overview.avgCost }} <span class="unit">/{{ statisticsType === 'day' ? '日' : '月' }}</span></div>
|
</div>
|
</div>
|
</el-col>
|
</el-row>
|
</div>
|
<!-- 图表区域 -->
|
<div class="charts-container">
|
<h2 class="section-header">
|
<el-icon class="header-icon">
|
<Histogram />
|
</el-icon>
|
能耗成本分析
|
</h2>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<div class="chart-card">
|
<div class="chart-title">能耗成本趋势</div>
|
<div ref="costChart"
|
class="chart-content"></div>
|
</div>
|
</el-col>
|
<el-col :span="12">
|
<div class="chart-card">
|
<div class="chart-title">能耗类型成本占比</div>
|
<div ref="typeChart"
|
class="chart-content"></div>
|
</div>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20"
|
style="margin-top: 20px;">
|
<el-col :span="12">
|
<div class="chart-card">
|
<div class="chart-title">能耗用途成本占比</div>
|
<div ref="purposeChart"
|
class="chart-content"></div>
|
</div>
|
</el-col>
|
<el-col :span="12">
|
<div class="chart-card">
|
<div class="chart-title">能耗单价对比</div>
|
<div ref="priceChart"
|
class="chart-content"></div>
|
</div>
|
</el-col>
|
</el-row>
|
</div>
|
<!-- 数据表格 -->
|
<div class="table-section">
|
<h2 class="section-header">
|
<el-icon class="header-icon">
|
<List />
|
</el-icon>
|
详细数据
|
</h2>
|
<el-table :data="tableData"
|
v-loading="tableLoading"
|
border>
|
<el-table-column type="index"
|
label="序号"
|
width="60"
|
align="center" />
|
<el-table-column prop="timePeriod"
|
:label="timeColumnLabel"
|
align="center" />
|
<el-table-column prop="energyType"
|
label="能耗类型"
|
width="100"
|
align="center">
|
<template #default="scope">
|
<el-tag :type="getEnergyTypeType(scope.row.energyType)">
|
{{ scope.row.energyType }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="energyPurpose"
|
label="能耗用途"
|
width="100"
|
align="center">
|
<template #default="scope">
|
<el-tag :type="scope.row.energyPurpose === '生产' ? 'primary' : 'info'">
|
{{ scope.row.energyPurpose }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="consumption"
|
label="用量"
|
align="right">
|
<template #default="scope">
|
<span class="consumption-value">{{ scope.row.consumption }}</span>
|
<span class="consumption-unit">{{ scope.row.unit }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="price"
|
label="单价(元)"
|
align="right">
|
<template #default="scope">
|
<span class="price-value">{{ scope.row.price }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="cost"
|
label="成本(元)"
|
align="right"
|
fixed="right">
|
<template #default="scope">
|
<span class="cost-value">¥{{ scope.row.cost }}</span>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
<!-- 分页 -->
|
<div class="pagination-container">
|
<el-pagination v-model:current-page="page.current"
|
v-model:page-size="page.size"
|
:page-sizes="[10, 20, 50, 100]"
|
:total="page.total"
|
layout="total, sizes, prev, pager, next, jumper"
|
@size-change="handleSizeChange"
|
@current-change="handleCurrentChange" />
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted, computed, nextTick } from "vue";
|
import { ElMessage } from "element-plus";
|
import {
|
Money,
|
DataLine,
|
TrendCharts,
|
Histogram,
|
List,
|
} from "@element-plus/icons-vue";
|
import * as echarts from "echarts";
|
import { energyCostStatistics } from "@/api/costAccounting/energyCosts";
|
|
// 统计维度:day-按日,month-按月
|
const statisticsType = ref("day");
|
|
// 搜索表单
|
const searchForm = reactive({
|
energyType: "",
|
energyPurpose: "",
|
dateRange: (() => {
|
// 默认最近7天
|
const end = new Date();
|
const start = new Date();
|
start.setDate(start.getDate() - 6);
|
return [start.toISOString().split("T")[0], end.toISOString().split("T")[0]];
|
})(),
|
monthRange: (() => {
|
// 默认最近3个月
|
const end = new Date();
|
const start = new Date();
|
start.setMonth(start.getMonth() - 2);
|
return [start.toISOString().slice(0, 7), end.toISOString().slice(0, 7)];
|
})(),
|
});
|
|
// 时间列标签
|
const timeColumnLabel = computed(() => {
|
return statisticsType.value === "day" ? "日期" : "月份";
|
});
|
|
// 统计概览
|
const overview = reactive({
|
totalCost: "0.00",
|
productionCost: "0.00",
|
officeCost: "0.00",
|
avgCost: "0.00",
|
});
|
|
// 表格数据
|
const tableData = ref([]);
|
const tableLoading = ref(false);
|
|
// 分页
|
const page = reactive({
|
current: 1,
|
size: 10,
|
total: 0,
|
});
|
|
// 图表引用
|
const costChart = ref(null);
|
const typeChart = ref(null);
|
const purposeChart = ref(null);
|
const priceChart = ref(null);
|
|
// 图表实例
|
let costChartInstance = null;
|
let typeChartInstance = null;
|
let purposeChartInstance = null;
|
let priceChartInstance = null;
|
|
// 获取能耗类型标签类型
|
const getEnergyTypeType = type => {
|
const typeMap = {
|
水: "primary",
|
电: "warning",
|
气: "success",
|
};
|
return typeMap[type] || "info";
|
};
|
|
// 初始化图表
|
const initCharts = () => {
|
nextTick(() => {
|
// 能耗成本趋势图
|
if (costChart.value) {
|
costChartInstance = echarts.init(costChart.value);
|
updateCostChart();
|
}
|
// 能耗类型成本占比图
|
if (typeChart.value) {
|
typeChartInstance = echarts.init(typeChart.value);
|
updateTypeChart();
|
}
|
// 能耗用途成本占比图
|
if (purposeChart.value) {
|
purposeChartInstance = echarts.init(purposeChart.value);
|
updatePurposeChart();
|
}
|
// 能耗单价对比图
|
if (priceChart.value) {
|
priceChartInstance = echarts.init(priceChart.value);
|
updatePriceChart();
|
}
|
});
|
};
|
|
// 更新能耗成本趋势图
|
const updateCostChart = () => {
|
const data = tableData.value;
|
const option = {
|
tooltip: {
|
trigger: "axis",
|
axisPointer: { type: "shadow" },
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
borderColor: "#409EFF",
|
borderWidth: 1,
|
textStyle: { color: "#303133" },
|
},
|
legend: {
|
data: ["生产能耗成本", "办公能耗成本"],
|
top: 0,
|
right: 10,
|
textStyle: { color: "#606266" },
|
},
|
grid: {
|
left: "3%",
|
right: "4%",
|
bottom: "10%",
|
top: "15%",
|
containLabel: true,
|
},
|
xAxis: {
|
type: "category",
|
data: data.map(item => item.timePeriod),
|
axisLabel: {
|
rotate: statisticsType.value === "day" ? 45 : 0,
|
color: "#606266",
|
},
|
axisLine: { lineStyle: { color: "#ebeef5" } },
|
splitLine: { show: false },
|
},
|
yAxis: {
|
type: "value",
|
name: "成本(元)",
|
nameTextStyle: { color: "#606266" },
|
axisLabel: { color: "#606266" },
|
axisLine: { show: false },
|
splitLine: { lineStyle: { color: "#f0f2f5" } },
|
},
|
series: [
|
{
|
name: "生产能耗成本",
|
type: "bar",
|
data: data.map(item => (item.energyPurpose === "生产" ? item.cost : 0)),
|
itemStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{ offset: 0, color: "#409EFF" },
|
{ offset: 1, color: "#66b1ff" },
|
]),
|
borderRadius: [4, 4, 0, 0],
|
},
|
animationDelay: function (idx) {
|
return idx * 100;
|
},
|
},
|
{
|
name: "办公能耗成本",
|
type: "bar",
|
data: data.map(item => (item.energyPurpose === "办公" ? item.cost : 0)),
|
itemStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{ offset: 0, color: "#67C23A" },
|
{ offset: 1, color: "#85ce61" },
|
]),
|
borderRadius: [4, 4, 0, 0],
|
},
|
animationDelay: function (idx) {
|
return idx * 100 + 100;
|
},
|
},
|
],
|
animationEasing: "elasticOut",
|
animationDelayUpdate: function (idx) {
|
return idx * 5;
|
},
|
};
|
costChartInstance.setOption(option);
|
};
|
|
// 更新能耗类型成本占比图
|
const updateTypeChart = () => {
|
const data = tableData.value;
|
const typeCosts = {};
|
|
data.forEach(item => {
|
if (!typeCosts[item.energyType]) {
|
typeCosts[item.energyType] = 0;
|
}
|
typeCosts[item.energyType] += parseFloat(item.cost);
|
});
|
|
const chartData = Object.entries(typeCosts).map(([name, value]) => ({
|
name,
|
value: value.toFixed(2),
|
}));
|
|
const option = {
|
tooltip: {
|
trigger: "item",
|
formatter: "{a} <br/>{b}: ¥{c} ({d}%)",
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
borderColor: "#409EFF",
|
borderWidth: 1,
|
textStyle: { color: "#303133" },
|
},
|
legend: {
|
orient: "horizontal",
|
bottom: 0,
|
textStyle: { color: "#606266" },
|
},
|
series: [
|
{
|
name: "能耗类型成本",
|
type: "pie",
|
radius: ["40%", "70%"],
|
center: ["50%", "40%"],
|
avoidLabelOverlap: false,
|
itemStyle: {
|
borderRadius: 4,
|
borderColor: "#fff",
|
borderWidth: 2,
|
},
|
label: {
|
show: false,
|
position: "center",
|
},
|
emphasis: {
|
label: {
|
show: true,
|
fontSize: "18",
|
fontWeight: "bold",
|
color: "#303133",
|
},
|
itemStyle: {
|
shadowBlur: 10,
|
shadowOffsetX: 0,
|
shadowColor: "rgba(0, 0, 0, 0.3)",
|
},
|
},
|
labelLine: {
|
show: false,
|
},
|
data: chartData,
|
},
|
],
|
color: ["#409EFF", "#67C23A", "#E6A23C"],
|
};
|
typeChartInstance.setOption(option);
|
};
|
|
// 更新能耗用途成本占比图
|
const updatePurposeChart = () => {
|
const data = tableData.value;
|
const purposeCosts = {
|
生产: 0,
|
办公: 0,
|
};
|
|
data.forEach(item => {
|
if (purposeCosts.hasOwnProperty(item.energyPurpose)) {
|
purposeCosts[item.energyPurpose] += parseFloat(item.cost);
|
}
|
});
|
|
const chartData = Object.entries(purposeCosts).map(([name, value]) => ({
|
name,
|
value: value.toFixed(2),
|
}));
|
|
const option = {
|
tooltip: {
|
trigger: "item",
|
formatter: "{a} <br/>{b}: ¥{c} ({d}%)",
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
borderColor: "#409EFF",
|
borderWidth: 1,
|
textStyle: { color: "#303133" },
|
},
|
legend: {
|
orient: "horizontal",
|
bottom: 0,
|
textStyle: { color: "#606266" },
|
},
|
series: [
|
{
|
name: "能耗用途成本",
|
type: "pie",
|
radius: "60%",
|
center: ["50%", "40%"],
|
data: chartData,
|
emphasis: {
|
itemStyle: {
|
shadowBlur: 10,
|
shadowOffsetX: 0,
|
shadowColor: "rgba(0, 0, 0, 0.3)",
|
},
|
},
|
label: {
|
show: true,
|
formatter: "{b}: {d}%",
|
color: "#606266",
|
},
|
labelLine: {
|
show: true,
|
lineStyle: { color: "#dcdfe6" },
|
},
|
},
|
],
|
color: ["#409EFF", "#67C23A"],
|
};
|
purposeChartInstance.setOption(option);
|
};
|
|
// 更新能耗单价对比图
|
const updatePriceChart = () => {
|
const data = tableData.value;
|
const priceData = {};
|
|
data.forEach(item => {
|
if (!priceData[item.energyType]) {
|
priceData[item.energyType] = {
|
生产: 0,
|
办公: 0,
|
};
|
}
|
if (priceData[item.energyType].hasOwnProperty(item.energyPurpose)) {
|
priceData[item.energyType][item.energyPurpose] = parseFloat(item.price);
|
}
|
});
|
|
const energyTypes = Object.keys(priceData);
|
const productionPrices = energyTypes.map(type => priceData[type].生产);
|
const officePrices = energyTypes.map(type => priceData[type].办公);
|
|
const option = {
|
tooltip: {
|
trigger: "axis",
|
axisPointer: { type: "shadow" },
|
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
borderColor: "#409EFF",
|
borderWidth: 1,
|
textStyle: { color: "#303133" },
|
},
|
legend: {
|
data: ["生产能耗单价", "办公能耗单价"],
|
top: 0,
|
right: 10,
|
textStyle: { color: "#606266" },
|
},
|
grid: {
|
left: "3%",
|
right: "4%",
|
bottom: "10%",
|
top: "15%",
|
containLabel: true,
|
},
|
xAxis: {
|
type: "category",
|
data: energyTypes,
|
axisLabel: { color: "#606266" },
|
axisLine: { lineStyle: { color: "#ebeef5" } },
|
splitLine: { show: false },
|
},
|
yAxis: {
|
type: "value",
|
name: "单价(元)",
|
nameTextStyle: { color: "#606266" },
|
axisLabel: { color: "#606266" },
|
axisLine: { show: false },
|
splitLine: { lineStyle: { color: "#f0f2f5" } },
|
},
|
series: [
|
{
|
name: "生产能耗单价",
|
type: "bar",
|
data: productionPrices,
|
itemStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{ offset: 0, color: "#409EFF" },
|
{ offset: 1, color: "#66b1ff" },
|
]),
|
borderRadius: [4, 4, 0, 0],
|
},
|
},
|
{
|
name: "办公能耗单价",
|
type: "bar",
|
data: officePrices,
|
itemStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{ offset: 0, color: "#67C23A" },
|
{ offset: 1, color: "#85ce61" },
|
]),
|
borderRadius: [4, 4, 0, 0],
|
},
|
},
|
],
|
};
|
priceChartInstance.setOption(option);
|
};
|
|
// 统计维度切换
|
const handleTypeChange = () => {
|
// 重置时间范围
|
if (statisticsType.value === "day") {
|
const end = new Date();
|
const start = new Date();
|
start.setDate(start.getDate() - 6);
|
searchForm.dateRange = [
|
start.toISOString().split("T")[0],
|
end.toISOString().split("T")[0],
|
];
|
} else {
|
const end = new Date();
|
const start = new Date();
|
start.setMonth(start.getMonth() - 2);
|
searchForm.monthRange = [
|
start.toISOString().slice(0, 7),
|
end.toISOString().slice(0, 7),
|
];
|
}
|
page.current = 1;
|
handleQuery();
|
};
|
|
// 查询
|
const handleQuery = () => {
|
tableLoading.value = true;
|
|
// 构造请求参数
|
const params = {
|
type: statisticsType.value,
|
energyType: searchForm.energyType || undefined,
|
energyPurpose: searchForm.energyPurpose || undefined,
|
};
|
|
if (statisticsType.value === "day") {
|
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
params.startDate = searchForm.dateRange[0];
|
params.endDate = searchForm.dateRange[1];
|
}
|
} else {
|
if (searchForm.monthRange && searchForm.monthRange.length === 2) {
|
params.startDate = searchForm.monthRange[0] + "-01";
|
params.endDate = searchForm.monthRange[1] + "-01";
|
}
|
}
|
|
// 调用接口获取数据
|
energyCostStatistics(params)
|
.then(res => {
|
if (res.code === 200) {
|
tableData.value = res.data.records || [];
|
page.total = res.data.total || 0;
|
|
// 更新统计概览数据
|
if (res.data.overview) {
|
overview.totalCost = res.data.overview.totalCost || "0.00";
|
overview.productionCost = res.data.overview.productionCost || "0.00";
|
overview.officeCost = res.data.overview.officeCost || "0.00";
|
overview.avgCost = res.data.overview.avgCost || "0.00";
|
}
|
} else {
|
ElMessage.error(res.message || "获取数据失败");
|
tableData.value = [];
|
page.total = 0;
|
}
|
})
|
.catch(err => {
|
console.error("获取数据异常:", err);
|
// 生成假数据
|
generateMockData();
|
})
|
.finally(() => {
|
tableLoading.value = false;
|
updateCharts();
|
});
|
};
|
|
// 生成假数据
|
const generateMockData = () => {
|
if (statisticsType.value === "day") {
|
// 生成最近7天的假数据
|
const mockData = [];
|
const today = new Date();
|
|
for (let i = 6; i >= 0; i--) {
|
const date = new Date(today);
|
date.setDate(date.getDate() - i);
|
const dateStr = date.toISOString().split("T")[0];
|
|
// 生产能耗数据
|
mockData.push({
|
timePeriod: dateStr,
|
energyType: "电",
|
energyPurpose: "生产",
|
consumption: (Math.random() * 1000 + 500).toFixed(2),
|
unit: "kWh",
|
price: "0.85",
|
cost: (Math.random() * 850 + 425).toFixed(2),
|
});
|
mockData.push({
|
timePeriod: dateStr,
|
energyType: "水",
|
energyPurpose: "生产",
|
consumption: (Math.random() * 500 + 200).toFixed(2),
|
unit: "m³",
|
price: "3.50",
|
cost: (Math.random() * 1750 + 700).toFixed(2),
|
});
|
mockData.push({
|
timePeriod: dateStr,
|
energyType: "气",
|
energyPurpose: "生产",
|
consumption: (Math.random() * 300 + 100).toFixed(2),
|
unit: "m³",
|
price: "2.80",
|
cost: (Math.random() * 840 + 280).toFixed(2),
|
});
|
|
// 办公能耗数据
|
mockData.push({
|
timePeriod: dateStr,
|
energyType: "电",
|
energyPurpose: "办公",
|
consumption: (Math.random() * 200 + 100).toFixed(2),
|
unit: "kWh",
|
price: "0.85",
|
cost: (Math.random() * 170 + 85).toFixed(2),
|
});
|
mockData.push({
|
timePeriod: dateStr,
|
energyType: "水",
|
energyPurpose: "办公",
|
consumption: (Math.random() * 50 + 20).toFixed(2),
|
unit: "m³",
|
price: "3.50",
|
cost: (Math.random() * 175 + 70).toFixed(2),
|
});
|
}
|
|
tableData.value = mockData;
|
page.total = mockData.length;
|
} else {
|
// 生成最近3个月的假数据
|
const mockData = [];
|
const today = new Date();
|
|
for (let i = 2; i >= 0; i--) {
|
const date = new Date(today);
|
date.setMonth(date.getMonth() - i);
|
const monthStr = date.toISOString().slice(0, 7);
|
|
// 生产能耗数据
|
mockData.push({
|
timePeriod: monthStr,
|
energyType: "电",
|
energyPurpose: "生产",
|
consumption: (Math.random() * 30000 + 15000).toFixed(2),
|
unit: "kWh",
|
price: "0.85",
|
cost: (Math.random() * 25500 + 12750).toFixed(2),
|
});
|
mockData.push({
|
timePeriod: monthStr,
|
energyType: "水",
|
energyPurpose: "生产",
|
consumption: (Math.random() * 15000 + 6000).toFixed(2),
|
unit: "m³",
|
price: "3.50",
|
cost: (Math.random() * 52500 + 21000).toFixed(2),
|
});
|
mockData.push({
|
timePeriod: monthStr,
|
energyType: "气",
|
energyPurpose: "生产",
|
consumption: (Math.random() * 9000 + 3000).toFixed(2),
|
unit: "m³",
|
price: "2.80",
|
cost: (Math.random() * 25200 + 8400).toFixed(2),
|
});
|
|
// 办公能耗数据
|
mockData.push({
|
timePeriod: monthStr,
|
energyType: "电",
|
energyPurpose: "办公",
|
consumption: (Math.random() * 6000 + 3000).toFixed(2),
|
unit: "kWh",
|
price: "0.85",
|
cost: (Math.random() * 5100 + 2550).toFixed(2),
|
});
|
mockData.push({
|
timePeriod: monthStr,
|
energyType: "水",
|
energyPurpose: "办公",
|
consumption: (Math.random() * 1500 + 600).toFixed(2),
|
unit: "m³",
|
price: "3.50",
|
cost: (Math.random() * 5250 + 2100).toFixed(2),
|
});
|
}
|
|
tableData.value = mockData;
|
page.total = mockData.length;
|
}
|
|
// 更新统计概览数据
|
calculateOverview();
|
};
|
|
// 计算统计概览数据
|
const calculateOverview = () => {
|
let totalCost = 0;
|
let productionCost = 0;
|
let officeCost = 0;
|
|
tableData.value.forEach(item => {
|
const cost = parseFloat(item.cost);
|
totalCost += cost;
|
if (item.energyPurpose === "生产") {
|
productionCost += cost;
|
} else if (item.energyPurpose === "办公") {
|
officeCost += cost;
|
}
|
});
|
|
overview.totalCost = totalCost.toFixed(2);
|
overview.productionCost = productionCost.toFixed(2);
|
overview.officeCost = officeCost.toFixed(2);
|
overview.avgCost = (totalCost / tableData.value.length).toFixed(2);
|
};
|
|
// 更新所有图表
|
const updateCharts = () => {
|
nextTick(() => {
|
if (costChartInstance) updateCostChart();
|
if (typeChartInstance) updateTypeChart();
|
if (purposeChartInstance) updatePurposeChart();
|
if (priceChartInstance) updatePriceChart();
|
});
|
};
|
|
// 重置
|
const handleReset = () => {
|
searchForm.energyType = "";
|
searchForm.energyPurpose = "";
|
if (statisticsType.value === "day") {
|
const end = new Date();
|
const start = new Date();
|
start.setDate(start.getDate() - 6);
|
searchForm.dateRange = [
|
start.toISOString().split("T")[0],
|
end.toISOString().split("T")[0],
|
];
|
} else {
|
const end = new Date();
|
const start = new Date();
|
start.setMonth(start.getMonth() - 2);
|
searchForm.monthRange = [
|
start.toISOString().slice(0, 7),
|
end.toISOString().slice(0, 7),
|
];
|
}
|
page.current = 1;
|
handleQuery();
|
};
|
|
// 导出
|
const handleExport = () => {
|
ElMessage.success("报表导出成功");
|
};
|
|
// 分页大小变化
|
const handleSizeChange = val => {
|
page.size = val;
|
};
|
|
// 页码变化
|
const handleCurrentChange = val => {
|
page.current = val;
|
};
|
|
// 窗口大小变化时重新渲染图表
|
const handleResize = () => {
|
costChartInstance && costChartInstance.resize();
|
typeChartInstance && typeChartInstance.resize();
|
purposeChartInstance && purposeChartInstance.resize();
|
priceChartInstance && priceChartInstance.resize();
|
};
|
|
onMounted(() => {
|
handleQuery();
|
initCharts();
|
window.addEventListener("resize", handleResize);
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.app-container {
|
padding: 20px;
|
}
|
|
.search_form {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
padding: 15px;
|
background-color: #f5f7fa;
|
border-radius: 8px;
|
}
|
|
.statistics-overview {
|
margin-bottom: 30px;
|
}
|
|
.charts-container {
|
margin-bottom: 30px;
|
}
|
|
.table-section {
|
margin-bottom: 20px;
|
}
|
|
.section-header {
|
display: flex;
|
align-items: center;
|
font-size: 18px;
|
font-weight: bold;
|
color: #303133;
|
margin-bottom: 15px;
|
padding-left: 10px;
|
border-left: 3px solid #409eff;
|
|
.header-icon {
|
margin-right: 8px;
|
color: #409eff;
|
}
|
}
|
|
.overview-card {
|
display: flex;
|
align-items: center;
|
padding: 20px;
|
border-radius: 4px;
|
background: #fff;
|
border: 1px solid #ebeef5;
|
|
&.blue-card {
|
background-color: #ecf5ff;
|
}
|
|
&.green-card {
|
background-color: #f0f9eb;
|
}
|
|
&.purple-card {
|
background-color: #f3f0ff;
|
}
|
|
&.gray-card {
|
background-color: #f5f7fa;
|
}
|
|
.overview-icon {
|
width: 40px;
|
height: 40px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
border-radius: 50%;
|
margin-right: 15px;
|
|
&.blue-icon {
|
background-color: #409eff;
|
color: #fff;
|
}
|
|
&.green-icon {
|
background-color: #67c23a;
|
color: #fff;
|
}
|
|
&.purple-icon {
|
background-color: #909399;
|
color: #fff;
|
}
|
|
&.gray-icon {
|
background-color: #909399;
|
color: #fff;
|
}
|
|
.el-icon {
|
font-size: 20px;
|
}
|
}
|
|
.overview-info {
|
flex: 1;
|
|
.overview-label {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 5px;
|
}
|
|
.overview-value {
|
font-size: 20px;
|
font-weight: bold;
|
color: #303133;
|
|
.unit {
|
font-size: 12px;
|
font-weight: normal;
|
color: #909399;
|
}
|
}
|
}
|
}
|
|
.chart-card {
|
background: #fff;
|
border-radius: 4px;
|
border: 1px solid #ebeef5;
|
padding: 20px;
|
|
.chart-title {
|
font-size: 14px;
|
font-weight: bold;
|
color: #303133;
|
margin-bottom: 15px;
|
padding-bottom: 10px;
|
border-bottom: 1px solid #ebeef5;
|
}
|
|
.chart-content {
|
height: 300px;
|
}
|
}
|
|
.consumption-value {
|
font-weight: bold;
|
color: #409eff;
|
}
|
|
.consumption-unit {
|
font-size: 12px;
|
color: #909399;
|
margin-left: 2px;
|
}
|
|
.price-value {
|
font-weight: bold;
|
color: #67c23a;
|
}
|
|
.cost-value {
|
font-weight: bold;
|
color: #f56c6c;
|
}
|
|
.pagination-container {
|
display: flex;
|
justify-content: flex-end;
|
}
|
</style>
|