add - ems产线能耗占比统计、耗能趋势分析、产线单耗对比、产品单耗对比页面

master
yinq 2 months ago
parent e0f8ca0ed3
commit 855e560e00

@ -0,0 +1,33 @@
import request from '@/utils/request'
export function listEnergyStatistics(query) {
return request({
url: '/energy/report/preview/productionLineEnergyConsumption',
method: 'get',
params: query
})
}
export function getEnergyConsumptionTrendAnalysis(query) {
return request({
url: '/energy/report/preview/energyConsumptionTrendAnalysis',
method: 'get',
params: query
})
}
export function getLinesConsumptionComparison(query) {
return request({
url: '/energy/report/preview/linesConsumptionComparison',
method: 'get',
params: query
})
}
export function getProductConsumptionComparison(query) {
return request({
url: '/energy/report/preview/productConsumptionComparison',
method: 'get',
params: query
})
}

@ -43,10 +43,10 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item> -->
<!-- <el-form-item label="等级" prop="grade">
<!-- <el-form-item label="统计维度" prop="grade">
<el-input
v-model="queryParams.grade"
placeholder="请输入等级"
placeholder="请输入统计维度"
clearable
@keyup.enter.native="handleQuery"
/>
@ -68,7 +68,7 @@
/>
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
@ -82,7 +82,8 @@
size="mini"
@click="handleAdd"
v-hasPermi="['base:buildInfo:add']"
>新增</el-button>
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
@ -91,10 +92,11 @@
icon="el-icon-sort"
size="mini"
@click="toggleExpandAll"
>展开/折叠</el-button>
>展开/折叠
</el-button>
</el-col>
<el-col :span="1.5">
{{list.length}}
{{ list.length }}
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@ -110,15 +112,20 @@
>
<!-- <el-table-column label="父级编号" prop="parentId" /> -->
<!-- <el-table-column label="建筑编号" align="center" prop="buildId" /> -->
<el-table-column label="建筑名称" align="center" prop="buildName" />
<el-table-column label="建筑名称" align="center" prop="buildName"/>
<el-table-column label="状态" align="center" prop="buildStatus">
<template slot-scope="scope">
<dict-tag :options="dict.type.monitorStatus" :value="scope.row.buildStatus"/>
</template>
</el-table-column>
<!-- <el-table-column label="祖级列表" align="center" prop="ancestors" />
<el-table-column label="等级" align="center" prop="grade" /> -->
<el-table-column label="备注" align="center" prop="remark" />
-->
<el-table-column label="统计维度" align="center" prop="grade">
<template slot-scope="scope">
<dict-tag :options="dict.type.ems_statistical_dimensions" :value="scope.row.grade"/>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark"/>
<!-- <el-table-column label="权限标识" align="center" prop="deptId" />
<el-table-column label="权限标识" align="center" prop="userId" /> -->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
@ -129,21 +136,24 @@
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['base:buildInfo:edit']"
>修改</el-button>
>修改
</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-plus"
@click="handleAdd(scope.row)"
v-hasPermi="['base:buildInfo:add']"
>新增</el-button>
>新增
</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['base:buildInfo:remove']"
>删除</el-button>
>删除
</el-button>
</template>
</el-table-column>
</el-table>
@ -152,13 +162,14 @@
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="auto">
<el-form-item label="父级编号" prop="parentId">
<treeselect v-model="form.parentId" :options="buildInfoOptions" :normalizer="normalizer" placeholder="请选择父级编号" />
<treeselect v-model="form.parentId" :options="buildInfoOptions" :normalizer="normalizer"
placeholder="请选择父级编号"/>
</el-form-item>
<!-- <el-form-item label="建筑编号" prop="buildId">
<el-input v-model="form.buildId" placeholder="请输入建筑编号" />
</el-form-item> -->
<el-form-item label="建筑名称" prop="buildName">
<el-input v-model="form.buildName" placeholder="请输入建筑名称" />
<el-input v-model="form.buildName" placeholder="请输入建筑名称"/>
</el-form-item>
<el-form-item label="状态" prop="buildStatus">
<el-radio-group v-model="form.buildStatus">
@ -166,14 +177,22 @@
v-for="dict in dict.type.monitorStatus"
:key="dict.value"
:label="parseInt(dict.value)"
>{{dict.label}}</el-radio>
>{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="统计维度" prop="grade">
<el-radio-group v-model="form.grade">
<el-radio
v-for="dict in dict.type.ems_statistical_dimensions"
:key="dict.value"
:label="parseInt(dict.value)"
>{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="等级" prop="grade">
<el-input v-model="form.grade" placeholder="请输入等级" />
</el-form-item> -->
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
<el-input v-model="form.remark" placeholder="请输入备注"/>
</el-form-item>
<!-- <el-form-item label="权限标识" prop="deptId">
<el-input v-model="form.deptId" placeholder="请输入权限标识" />
@ -191,13 +210,19 @@
</template>
<script>
import { listBuildInfo, getBuildInfo, delBuildInfo, addBuildInfo, updateBuildInfo } from "@/api/energy/energy/base/buildInfo";
import {
listBuildInfo,
getBuildInfo,
delBuildInfo,
addBuildInfo,
updateBuildInfo
} from "@/api/energy/energy/base/buildInfo";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "BuildInfo",
dicts: ['monitorStatus'],
dicts: ['monitorStatus', 'ems_statistical_dimensions'],
components: {
Treeselect
},
@ -235,8 +260,7 @@ export default {
//
form: {},
//
rules: {
}
rules: {}
};
},
created() {
@ -263,11 +287,11 @@ export default {
children: node.children
};
},
/** 查询建筑信息管理下拉树结构 */
/** 查询建筑信息管理下拉树结构 */
getTreeselect() {
listBuildInfo().then(response => {
this.buildInfoOptions = [];
const data = { objid: 0, buildName: '顶级节点', children: [] };
const data = {objid: 0, buildName: '顶级节点', children: []};
data.children = this.handleTree(response.data, "objid", "parentId");
this.buildInfoOptions.push(data);
});
@ -361,12 +385,13 @@ export default {
},
/** 删除按钮操作 */
handleDelete(row) {
this.$modal.confirm('是否确认删除建筑信息管理编号为"' + row.objid + '"的数据项?').then(function() {
this.$modal.confirm('是否确认删除建筑信息管理编号为"' + row.objid + '"的数据项?').then(function () {
return delBuildInfo(row.objid);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
}).catch(() => {
});
}
}
};

@ -0,0 +1,571 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!--左侧部门数据-->
<el-col :span="6" :xs="24">
<div class="head-container">
<el-tag
v-for="item in dynamicTags"
:key="item.monitorId"
closable
:disable-transitions="false"
@close="handleClose(item.monitorId)"
>
{{ item.label }}
</el-tag>
<el-input
v-model="deptName"
placeholder="请输入计量设备名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
</div>
<div class="head-container tree">
<el-tree
ref="tree"
:data="deptOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
node-key="id"
default-expand-all
highlight-current
@node-click="handleNodeClick"
/>
</div>
</el-col>
<!--右侧用户数据-->
<el-col :span="18" :xs="24">
<!--头部搜索条件输入框 -->
<el-form
ref="queryForm"
:model="queryParams"
:inline="true"
size="small"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="采集时间">
<el-date-picker
v-model="queryParams.startTime"
:type="getDatePickerType"
value-format="yyyy-MM-dd HH:mm:ss"
:placeholder="getDatePickerPlaceholder"
style="width: 240px"
@change="handleQuery"
/>
<span class="mx-2">-</span>
<el-date-picker
v-model="queryParams.endTime"
:type="getDatePickerType"
value-format="yyyy-MM-dd HH:mm:ss"
:placeholder="getDatePickerPlaceholder"
style="width: 240px"
@change="handleQuery"
/>
</el-form-item>
<el-form-item label="时间范围" prop="status">
<el-select
v-model="queryParams.type"
placeholder="请选择"
clearable
style="width: 240px"
@change="handleTimeRangeChange"
>
<el-option
v-for="dict in timeRangeOptions"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
<!--柱状图 -->
<el-card style="height: 750px; margin-bottom: 20px">
<div style="width: 100%; height: 360px" id="card1"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import {getEnergyConsumptionTrendAnalysis} from '@/api/energy/record/statistics'
import {getToken} from "@/utils/auth";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import * as echarts from "echarts";
import {treeDat} from "@/api/energy/energy/base/typeRelation";
export default {
name: "EnergyConsumptionTrendAnalysis",
dicts: ["sys_normal_disable", "sys_user_sex"],
components: {Treeselect},
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
userList: null,
//
title: "",
//
deptOptions: [],
//
open: false,
//
deptName: undefined,
//
initPassword: undefined,
//
dateRange: [],
dateRangeone: '',
dateRangetwo: '',
//
postOptions: [],
//
roleOptions: [],
//
form: {},
defaultProps: {
children: "children",
label: "lable",
},
//
upload: {
//
open: false,
//
title: "",
//
isUploading: false,
//
updateSupport: 0,
//
headers: {Authorization: "Bearer " + getToken()},
//
url: process.env.VUE_APP_BASE_API + "/system/user/importData",
},
//
queryParams: {
type: 'hour',
startTime: '',
endTime: '',
energyType: 2,
monitorIds: '',
},
//
columns: [
{key: 0, label: `计量设备编号`, visible: true},
{key: 1, label: `计量设备名称`, visible: true},
{key: 2, label: `采集时间`, visible: true},
{key: 3, label: `功率因数`, visible: true},
{key: 4, label: `仪表值(kW·h)`, visible: true},
{key: 5, label: `采集方式`, visible: true},
],
//
dict: [
{label: "时", value: "hour"},
{label: "日", value: "day"},
{label: "月", value: "month"},
{label: "年", value: "year"},
],
//+线
option: {
title: {
text: "环比分析",
left: "center",
},
tooltip: {
trigger: "axis",
formatter: function (params) {
var relvsl = '';
for (var i = 0, l = params.length; i < l; i++) {
relvsl += params[i].marker + ' ' + params[i].seriesName + "(KW-h):" + params[i].data + '<br>'
}
return params[0].name + '<br>' + relvsl
},
axisPointer: {
type: "cross",
crossStyle: {
color: "#999",
},
},
},
legend: {
data: [],
right: "5%",
},
xAxis: [
{
type: "category",
data: [],
axisPointer: {
type: "shadow",
},
},
],
yAxis: [
{
type: "value",
name: "单位:KW-h",
axisLine: {
show: true,
}
},
],
series: [
{
name: "",
type: "bar",
tooltip: {
valueFormatter: function (value) {
return value;
},
},
data: [],
},
],
},
optionTwo: {
title: {
text: "能耗曲线",
left: "center",
},
tooltip: {
trigger: "axis",
formatter: function (params) {
var relvsl = '';
for (var i = 0, l = params.length; i < l; i++) {
relvsl += params[i].marker + ' ' + params[i].seriesName + "(KW-h):" + params[i].data + '<br>'
}
return params[0].name + '<br>' + relvsl
},
},
legend: {
data: [],
right: '5%',
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: [],
},
yAxis: {
type: "value",
name: "单位:KW-h",
axisLine: {
show: true,
}
},
series: [],
},
dynamicTags: [],
//
timeRangeOptions: [
{label: "时", value: "hour"},
{label: "日", value: "day"},
{label: "月", value: "month"},
{label: "年", value: "year"},
],
//
chartOptions: {
lineChart: {
title: {text: "能耗曲线", left: "center"},
tooltip: {trigger: "axis", formatter: this.formatTooltip},
legend: {data: [], right: '5%'},
grid: {left: "3%", right: "4%", bottom: "3%", containLabel: true},
xAxis: {type: "category", boundaryGap: false, data: []},
yAxis: {type: "value", name: "单位:KW-h", axisLine: {show: true}},
series: []
}
}
};
},
watch: {
//
deptName(val) {
this.$refs.tree.filter(val);
},
},
created() {
this.initDateTime();
this.getDeptTree();
},
mounted() {
this.initCharts();
},
methods: {
//
initDateTime() {
const now = new Date();
const startTime = new Date(now.setHours(0, 0, 0, 0));
const endTime = new Date(now.setHours(23, 59, 59, 999));
this.queryParams.startTime = this.formatDateTime(startTime, this.queryParams.type);
this.queryParams.endTime = this.formatDateTime(endTime, this.queryParams.type);
},
initCharts() {
const lineChart = echarts.init(document.getElementById("card1"));
lineChart.setOption(this.chartOptions.lineChart);
},
//
formatDateTime(date, type) {
const pad = num => String(num).padStart(2, '0');
const year = date.getFullYear();
const month = pad(date.getMonth() + 1);
const day = pad(date.getDate());
const hours = pad(date.getHours());
const minutes = pad(date.getMinutes());
const seconds = pad(date.getSeconds());
switch(type) {
case 'year':
return `${year}-01-01 00:00:00`;
case 'month':
return `${year}-${month}-01 00:00:00`;
case 'day':
return `${year}-${month}-${day} 00:00:00`;
case 'hour':
default:
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
},
//
getEndDateTime(date, type) {
const pad = num => String(num).padStart(2, '0');
const year = date.getFullYear();
const month = pad(date.getMonth() + 1);
const day = pad(date.getDate());
switch(type) {
case 'year':
return `${year}-12-31 23:59:59`;
case 'month':
//
const lastDay = new Date(year, date.getMonth() + 1, 0).getDate();
return `${year}-${month}-${pad(lastDay)} 23:59:59`;
case 'day':
return `${year}-${month}-${day} 23:59:59`;
case 'hour':
default:
return `${year}-${month}-${day} 23:59:59`;
}
},
//
handleTimeRangeChange(type) {
const now = new Date();
const startTime = new Date(now.setHours(0, 0, 0, 0));
const endTime = new Date(now.setHours(23, 59, 59, 999));
this.queryParams.startTime = this.formatDateTime(startTime, type);
this.queryParams.endTime = this.getEndDateTime(endTime, type);
this.handleQuery();
},
//
tranListToTreeData(list) {
//
const treeList = []
// 使
const map = {}
// id
list.forEach(item => {
if (!item.children) {
item.children = []
}
map[item.id] = item
})
list.forEach(item => {
//
// children
// treeList
const parent = map[item.pId]
// item
if (parent) {
parent.children.push(item)
} else {
//
treeList.push(item)
}
})
//
return treeList
},
/** 查询部门下拉树结构 */
getDeptTree() {
treeDat().then((response) => {
response.map((item, index) => {
item,
item.oid = item.id,
item.lable = item.name
})
this.deptOptions = this.tranListToTreeData(response);
console.log(this.deptOptions)
//this.deptOptions = response.data;
});
},
//
filterNode(value, data) {
if (!value) return true;
return data.lable.indexOf(value) !== -1;
},
//
handleNodeClick(data) {
console.log("data", data);
this.queryParams.monitorId = data.id;
this.dynamicTags.push({label: data.name, value: data.id});
this.handleQuery();
},
//tag
handleClose(item) {
this.dynamicTags.splice(this.dynamicTags.indexOf(item), 1);
let querydata = [];
this.dynamicTags.map((item) => {
querydata.push(item.value);
});
querydata.toString();
this.handleQuery();
},
datetimechangetwo() {
this.handleQuery();
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
userId: undefined,
monitorId: undefined,
userName: undefined,
nickName: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: "0",
remark: undefined,
postIds: [],
roleIds: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
async handleQuery() {
const monitorIds = this.dynamicTags.map(tag => tag.value).join(',');
// 线
const response = await getEnergyConsumptionTrendAnalysis({
type: this.queryParams.type,
startTime: this.queryParams.startTime,
endTime: this.queryParams.endTime,
monitorIds,
energyType: 2
});
// 线
this.updateEnergyChart(response.data);
},
// 线
updateEnergyChart(data) {
const {monitorNameList, timeList, monitorInfoMap} = data;
const seriesData = Object.entries(monitorInfoMap).map(([_, values], index) => ({
name: monitorNameList[index],
type: "line",
stack: "Total",
data: values
}));
const lineChart = echarts.init(document.getElementById("card1"));
this.chartOptions.lineChart.legend.data = monitorNameList;
this.chartOptions.lineChart.xAxis.data = timeList;
this.chartOptions.lineChart.series = seriesData;
lineChart.setOption(this.chartOptions.lineChart);
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.datetimechangetwo = '',
this.resetForm("queryForm");
this.queryParams.monitorId = undefined;
this.$refs.tree.setCurrentKey(null);
this.handleQuery();
},
},
computed: {
getDatePickerType() {
switch(this.queryParams.type) {
case 'year':
return 'year';
case 'month':
return 'month';
case 'day':
return 'date';
case 'hour':
default:
return 'datetime';
}
},
getDatePickerPlaceholder() {
switch(this.queryParams.type) {
case 'year':
return '选择年份';
case 'month':
return '选择月份';
case 'day':
return '选择日期';
case 'hour':
default:
return '选择日期时间';
}
}
},
};
</script>
<style lang="scss" scoped>
.tree {
width: 220px;
::v-deep .el-tree-node {
white-space: normal; //!!!!!!!!!
.el-tree-node__content {
height: 100%;
align-items: start;
}
}
}
.mx-2 {
margin: 0 8px;
}
</style>

@ -0,0 +1,301 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="订单编号" prop="workorderCode">
<el-input
v-model="queryParams.workorderCode"
placeholder="请输入订单编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品编号" prop="productCode">
<el-input
v-model="queryParams.productCode"
placeholder="请输入产品编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产品名称" prop="productName">
<el-input
v-model="queryParams.productName"
placeholder="请输入产品名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="订单生产日期" label-width="100px">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- <el-row :gutter="10" class="mb8">-->
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="warning"-->
<!-- plain-->
<!-- icon="el-icon-download"-->
<!-- size="mini"-->
<!-- @click="handleExport"-->
<!-- v-hasPermi="['energy:orderenergy:export']"-->
<!-- >导出</el-button>-->
<!-- </el-col>-->
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>-->
<!-- </el-row>-->
<!-- 图表容器 -->
<div ref="chartContainer" style="width: 100%; height: 400px; margin: 20px 0;"></div>
</div>
</template>
<script>
import { getLinesConsumptionComparison } from "@/api/energy/record/statistics";
import * as echarts from 'echarts';
export default {
name: "LinesConsumptionComparison",
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
OrderEnergyList: [],
//
dateRange: [
this.parseTime(new Date().getTime() - 6 * 24 * 60 * 60 * 1000, '{y}-{m}-{d}'),
this.parseTime(new Date(), '{y}-{m}-{d}')
],
//
queryParams: {
pageNum: 1,
pageSize: 10,
factoryId: null,
productCode: null,
productName: null,
workorderName: null,
equipmentName: null,
electricityNo: null,
attr1: null,
attr2: null,
attr3: null,
attr4: null,
},
chart: null,
};
},
created() {
this.getList();
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (this.chart) {
this.chart.dispose();
}
},
methods: {
/** 初始化图表 */
initChart() {
this.chart = echarts.init(this.$refs.chartContainer);
this.updateChart();
},
/** 更新图表数据 */
updateChart() {
if (!this.chart) return;
const xData = this.OrderEnergyList.map(item => item.workorderName);
const yData = this.OrderEnergyList.map(item => item.singleKw);
const option = {
title: {
text: '产线单耗对比分析',
subtext: '单位KW·h/箱',
left: 'center',
top: 10,
textStyle: {
fontSize: 18,
fontWeight: 'bold'
},
subtextStyle: {
fontSize: 12,
color: '#666'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderColor: '#ccc',
borderWidth: 1,
textStyle: {
color: '#333'
},
formatter: function(params) {
const data = params[0];
return `<div style="padding: 3px;">
<div style="font-weight: bold; margin-bottom: 5px;">${data.name}</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="display: inline-block; width: 10px; height: 10px; background: #409EFF; margin-right: 5px;"></span>
<span>单耗</span>
<span style="font-weight: bold; margin-left: 5px;">${data.value.toFixed(2)} KW·h/</span>
</div>
</div>`;
}
},
grid: {
left: '6%',
right: '9%',
bottom: '8%',
top: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: xData,
axisLabel: {
interval: 0,
rotate: 30,
fontSize: 12,
color: '#666'
},
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisTick: {
alignWithLabel: true
},
name: '产线名称',
nameTextStyle: {
padding: [10, 0, 0, 0],
color: '#666'
}
},
yAxis: {
type: 'value',
name: '单耗KW·h/箱)',
nameTextStyle: {
padding: [0, 0, 10, 0],
color: '#666'
},
axisLabel: {
formatter: '{value}',
color: '#666'
},
splitLine: {
lineStyle: {
type: 'dashed',
color: '#eee'
}
},
axisLine: {
show: true,
lineStyle: {
color: '#ddd'
}
}
},
series: [{
name: '单耗',
type: 'bar',
data: yData,
barWidth: '40%',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#409EFF' },
{ offset: 1, color: '#188df0' }
]),
borderRadius: [4, 4, 0, 0]
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2378f7' },
{ offset: 0.7, color: '#2378f7' },
{ offset: 1, color: '#83bff6' }
])
}
},
label: {
show: true,
position: 'top',
distance: 10,
formatter: '{c} KW·h/箱',
fontSize: 12,
color: '#666'
}
}]
};
this.chart.setOption(option);
},
/** 查询列表 */
getList() {
this.loading = true;
getLinesConsumptionComparison(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
//
const data = response.data.data;
this.OrderEnergyList = Object.entries(data).map(([lineName, value]) => ({
workorderName: lineName,
singleKw: value
}));
this.total = this.OrderEnergyList.length;
this.loading = false;
this.updateChart();
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 导出按钮操作 */
handleExport() {
this.download('energy/report/analysis/orderenergy/export', {
...this.queryParams
}, `单耗分析_${new Date().getTime()}.xlsx`)
}
}
};
</script>
<style scoped>
.chart-container {
margin-top: 20px;
}
</style>

@ -0,0 +1,307 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="订单编号" prop="workorderCode">
<el-input
v-model="queryParams.workorderCode"
placeholder="请输入订单编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="产线名称" prop="workorderName">
<el-select v-model="queryParams.workorderName" placeholder="请选择产线名称" clearable>
<el-option
v-for="item in lineBodys"
:key="item.lineCode"
:label="item.lineName"
:value="item.lineCode"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单生产日期" label-width="100px">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- <el-row :gutter="10" class="mb8">-->
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="warning"-->
<!-- plain-->
<!-- icon="el-icon-download"-->
<!-- size="mini"-->
<!-- @click="handleExport"-->
<!-- v-hasPermi="['energy:orderenergy:export']"-->
<!-- >导出</el-button>-->
<!-- </el-col>-->
<!-- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>-->
<!-- </el-row>-->
<!-- 图表容器 -->
<div ref="chartContainer" style="width: 100%; height: 400px; margin: 20px 0;"></div>
<!-- <pagination-->
<!-- v-show="total>0"-->
<!-- :total="total"-->
<!-- :page.sync="queryParams.pageNum"-->
<!-- :limit.sync="queryParams.pageSize"-->
<!-- @pagination="getList"-->
<!-- />-->
</div>
</template>
<script>
import { getProductConsumptionComparison } from "@/api/energy/record/statistics";
import * as echarts from 'echarts';
import {selectLineBody} from "@/api/mes/mesLine";
export default {
name: "productConsumptionComparison",
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
OrderEnergyList: [],
lineBodys: [],
//
dateRange: [
this.parseTime(new Date().getTime() - 6 * 24 * 60 * 60 * 1000, '{y}-{m}-{d}'),
this.parseTime(new Date(), '{y}-{m}-{d}')
],
//
queryParams: {
pageNum: 1,
pageSize: 10,
factoryId: null,
productCode: null,
productName: null,
workorderName: null,
equipmentName: null,
electricityNo: null,
attr1: null,
attr2: null,
attr3: null,
attr4: null,
},
chart: null,
};
},
created() {
selectLineBody(this.queryParams).then((response) => {
this.lineBodys = response.rows;
});
this.getList();
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (this.chart) {
this.chart.dispose();
}
},
methods: {
/** 初始化图表 */
initChart() {
this.chart = echarts.init(this.$refs.chartContainer);
this.updateChart();
},
/** 更新图表数据 */
updateChart() {
if (!this.chart) return;
const xData = this.OrderEnergyList.map(item => item.workorderName);
const yData = this.OrderEnergyList.map(item => item.singleKw);
const option = {
title: {
text: '产品单耗对比分析',
subtext: '单位KW·h/箱',
left: 'center',
top: 10,
textStyle: {
fontSize: 18,
fontWeight: 'bold'
},
subtextStyle: {
fontSize: 12,
color: '#666'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderColor: '#ccc',
borderWidth: 1,
textStyle: {
color: '#333'
},
formatter: function(params) {
const data = params[0];
return `<div style="padding: 3px;">
<div style="font-weight: bold; margin-bottom: 5px;">${data.name}</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="display: inline-block; width: 10px; height: 10px; background: #409EFF; margin-right: 5px;"></span>
<span>单耗</span>
<span style="font-weight: bold; margin-left: 5px;">${data.value.toFixed(2)} KW·h/</span>
</div>
</div>`;
}
},
grid: {
left: '6%',
right: '9%',
bottom: '8%',
top: '15%',
containLabel: true
},
xAxis: {
type: 'category',
data: xData,
axisLabel: {
interval: 0,
rotate: 30,
fontSize: 12,
color: '#666'
},
axisLine: {
lineStyle: {
color: '#ddd'
}
},
axisTick: {
alignWithLabel: true
},
name: '产品名称',
nameTextStyle: {
padding: [10, 0, 0, 0],
color: '#666'
}
},
yAxis: {
type: 'value',
name: '单耗KW·h/箱)',
nameTextStyle: {
padding: [0, 0, 10, 0],
color: '#666'
},
axisLabel: {
formatter: '{value}',
color: '#666'
},
splitLine: {
lineStyle: {
type: 'dashed',
color: '#eee'
}
},
axisLine: {
show: true,
lineStyle: {
color: '#ddd'
}
}
},
series: [{
name: '单耗',
type: 'bar',
data: yData,
barWidth: '40%',
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#409EFF' },
{ offset: 1, color: '#188df0' }
]),
borderRadius: [4, 4, 0, 0]
},
emphasis: {
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#2378f7' },
{ offset: 0.7, color: '#2378f7' },
{ offset: 1, color: '#83bff6' }
])
}
},
label: {
show: true,
position: 'top',
distance: 10,
formatter: '{c} KW·h/箱',
fontSize: 12,
color: '#666'
}
}]
};
this.chart.setOption(option);
},
/** 查询列表 */
getList() {
this.loading = true;
getProductConsumptionComparison(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
//
const data = response.data.data;
this.OrderEnergyList = Object.entries(data).map(([lineName, value]) => ({
workorderName: lineName,
singleKw: value
}));
this.total = this.OrderEnergyList.length;
this.loading = false;
this.updateChart();
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 导出按钮操作 */
handleExport() {
this.download('energy/report/analysis/orderenergy/export', {
...this.queryParams
}, `单耗分析_${new Date().getTime()}.xlsx`)
}
}
};
</script>
<style scoped>
.chart-container {
margin-top: 20px;
}
</style>

@ -0,0 +1,260 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="统计时间">
<el-date-picker
v-model="daterangeCollectTime"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item label="统计单元" prop="unitId">
<el-select v-model="queryParams.unitId" placeholder="统计单元" clearable>
<el-option
v-for="dict in dict.type.ems_statistical_dimensions"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<div class="content-container">
<div class="chart-container">
<div ref="pieChart" style="width: 100%; height: 400px;"></div>
</div>
<div class="table-container">
<el-table v-loading="loading" :data="statisticsList" style="width: 100%">
<el-table-column label="统计单元" align="center" prop="buildName"/>
<el-table-column label="耗电量(kWh)" align="center" prop="expend"/>
<el-table-column label="占比" align="center" prop="percentage">
<template slot-scope="scope">
{{ scope.row.percentage }}%
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</template>
<script>
import { listEnergyStatistics } from '@/api/energy/record/statistics'
import * as echarts from 'echarts'
export default {
name: 'EnergyReport',
dicts: ["ems_statistical_dimensions"],
data() {
return {
//
loading: true,
//
showSearch: true,
//
daterangeCollectTime: [this.getToday(), this.getToday()],
//
queryParams: {
pageNum: 1,
pageSize: 10,
unitId: null
},
//
statisticsList: [],
//
chart: null
}
},
mounted() {
this.initChart()
this.getList()
},
beforeDestroy() {
if (this.chart) {
this.chart.dispose()
}
},
methods: {
/** 获取当天日期 */
getToday() {
const date = new Date()
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
/** 初始化图表 */
initChart() {
this.chart = echarts.init(this.$refs.pieChart)
},
/** 更新图表数据 */
updateChart() {
const statisticsList = this.statisticsList
const option = {
title: {
text: '生产线能耗统计',
subtext: '单位kWh',
left: 'center',
top: 20,
textStyle: {
fontSize: 24,
fontWeight: 'bold',
color: '#333'
},
subtextStyle: {
fontSize: 14,
color: '#666'
}
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} kWh ({d}%)',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderColor: '#ccc',
borderWidth: 1,
textStyle: {
color: '#333'
}
},
legend: {
orient: 'vertical',
right: '5%',
top: 'middle',
itemWidth: 10,
itemHeight: 10,
textStyle: {
color: '#666',
fontSize: 12
},
formatter: function(name) {
const item = statisticsList.find(item => item.buildName === name)
return `${name} ${item.percentage}%`
}
},
series: [
{
name: '能耗占比',
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false
},
emphasis: {
label: {
show: true,
fontSize: 14,
fontWeight: 'bold'
},
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
data: this.statisticsList.map(item => ({
value: item.expend,
name: item.buildName,
itemStyle: {
color: this.getRandomColor()
}
}))
}
]
}
this.chart.setOption(option)
},
/** 生成随机颜色 */
getRandomColor() {
const colors = [
'#5470c6',
'#91cc75',
'#fac858',
'#ee6666',
'#73c0de',
'#3ba272',
'#fc8452',
'#9a60b4',
'#ea7ccc'
]
return colors[Math.floor(Math.random() * colors.length)]
},
/** 查询统计数据 */
getList() {
this.loading = true
this.queryParams.params = {}
if (null != this.daterangeCollectTime && '' != this.daterangeCollectTime) {
this.queryParams.beginTime = this.daterangeCollectTime[0]
this.queryParams.endTime = this.daterangeCollectTime[1]
}
listEnergyStatistics(this.queryParams).then(response => {
this.statisticsList = response.data.energyList
//
const totalExpend = this.statisticsList.reduce((sum, item) => sum + item.expend, 0)
//
this.statisticsList = this.statisticsList.map(item => ({
...item,
percentage: ((item.expend / totalExpend) * 100).toFixed(2)
}))
this.loading = false
this.updateChart()
})
},
/** 搜索按钮操作 */
handleQuery() {
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.daterangeCollectTime = [this.getToday(), this.getToday()]
this.resetForm('queryForm')
this.handleQuery()
}
}
}
</script>
<style scoped>
.app-container {
padding: 20px;
height: calc(100vh - 84px);
display: flex;
flex-direction: column;
}
.content-container {
flex: 1;
display: flex;
flex-direction: column;
gap: 20px;
overflow: auto;
}
.chart-container {
background: #fff;
padding: 20px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
min-height: 440px;
}
.table-container {
background: #fff;
padding: 20px;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
</style>
Loading…
Cancel
Save