Files
sk_fems_ui/pages/ems/base/MonthlyReportReadPage.vue
2025-07-12 15:13:46 +09:00

818 lines
19 KiB
Vue

<template>
<div class="l-layout">
<v-row ref="searchFilter">
<v-col :cols="12">
<v-card class="searchFilter">
<v-row class="search-box" align="center" no-gutters>
<v-col :cols="1">
<label for="" class="search-box-label">
<v-icon x-small color="primary" class="mr-1"
>mdi-record-circle</v-icon
>
기간
</label>
</v-col>
<v-col :cols="2">
<v-text-field
id="startpicker"
ref="startpicker"
v-model="targetDateTimeObject.value"
:class="'v-input__custom'"
readonly
outlined
:hide-details="true"
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div
ref="startpicker-container"
id="startpicker-container"
></div>
</template>
</v-text-field>
</v-col>
<v-spacer></v-spacer>
<v-col :cols="2" class="d-flex justify-end align-center">
<BtnSearch @click="search" class="mr-1" />
<div id="btnExeclDownload">
<v-btn :ripple="false" @click="downloadExcelFile">액셀</v-btn>
</div>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
<v-row ref="contents">
<v-col :cols="12" style="height: 100%">
<v-card class="pb-5">
<v-row class="pa-5">
<v-col :cols="12">
<v-icon x-small color="primary" class="mr-1"
>mdi-record-circle</v-icon
>
<span class="custom-title-4">권역 종합</span>
</v-col>
</v-row>
<v-row>
<v-col :cols="12">
<div
ref="gridParent01"
class="px-5"
:style="{ height: 'calc(25vh - 90px)' }"
>
<component
:is="loadGrid01 ? 'Grid' : null"
:gridName="gridName01"
:parentPrgmId="myPrgmId"
ref="rowGrid01"
/>
</div>
</v-col>
</v-row>
<v-row class="pa-5">
<v-col :cols="12">
<v-icon x-small color="primary" class="mr-1"
>mdi-record-circle</v-icon
>
<span class="custom-title-4">에너지원별 종합</span>
</v-col>
</v-row>
<v-row>
<v-col :cols="12">
<div
ref="gridParent02"
class="px-5"
:style="{ height: 'calc(25vh - 90px)' }"
>
<component
:is="loadGrid02 ? 'Grid' : null"
:gridName="gridName02"
:parentPrgmId="myPrgmId"
ref="rowGrid02"
/>
</div>
</v-col>
</v-row>
<v-row class="pa-5">
<v-col :cols="12">
<v-icon x-small color="primary" class="mr-1"
>mdi-record-circle</v-icon
>
<span class="custom-title-4">조직별 종합</span>
</v-col>
</v-row>
<v-row>
<v-col :cols="12">
<div
ref="gridParent03"
class="px-5"
:style="{ height: 'calc(25vh - 90px)' }"
>
<component
:is="loadGrid03 ? 'Grid' : null"
:gridName="gridName03"
:parentPrgmId="myPrgmId"
ref="rowGrid03"
/>
</div>
</v-col>
</v-row>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import mixinGlobal from '@/mixin/global.js';
import SelectDate from '~/components/common/select/SelectDate';
import SelectDateVc from '~/components/common/select/SelectDateVc';
import FtnPlcMultiPop from '~/components/common/modal/FtnPlcMultiPop';
import TuiDatepicker from 'tui-date-picker';
import BtnSearch from '~/components/common/button/BtnSearch';
import BtnExcelDownload from '~/components/common/button/BtnExcelDownload';
import Utility from '~/plugins/utility';
import Grid from '~/components/common/Grid';
import XLSX from 'xlsx';
let myTitle;
let myPrgmId;
export default {
mixins: [mixinGlobal],
async asyncData(context) {
const myState = context.store.state;
myPrgmId = context.route.query.prgmId;
await context.store.commit('setActiveMenuInfo', myState.menuData[myPrgmId]);
myTitle = await myState.activeMenuInfo.menuNm;
},
meta: {
title: () => {
return myTitle;
},
prgmId: myPrgmId,
closable: true,
},
components: {
SelectDate,
SelectDateVc,
FtnPlcMultiPop,
BtnSearch,
BtnExcelDownload,
Utility,
Grid,
},
data() {
return {
myPrgmId: myPrgmId,
targetDateTimeObject: {
value: new Date().toISOString().substr(0, 7),
},
startDatepickerInstance: null,
loadGrid01: false,
loadGrid02: false,
loadGrid03: false,
gridName01: 'rowGrid01',
gridName02: 'rowGrid02',
gridName03: 'rowGrid03',
};
},
computed: {
...mapState({
pageData: state => state.pageData[myPrgmId],
}),
chkIsFind() {
return this.pageData.isFind;
},
},
watch: {
chkIsFind(val) {
if (val) this.search();
},
'targetDateTimeObject.value': function() {
this.setPageData({ isFind: true });
},
},
beforeCreate() {
myPrgmId = this.$route.query.prgmId;
this.$store.dispatch('chkOpenTabList', {
key: 'create',
prgmId: myPrgmId,
defaultData: defaultData,
});
},
mounted() {
this.init();
},
methods: {
...mapMutations({
setPageData: 'setPageData',
setGridData: 'setGridData',
setGridColumn: 'setGridColumn',
setGridOption: 'setGridOption',
}),
...mapActions({
postApi: 'modules/list/postApi',
postUpdateApi: 'modules/list/postUpdateApi',
postApiReturn: 'modules/list/postApiReturn',
chkOpenTabList: 'chkOpenTabList',
}),
async init() {
await this.gridInit();
this.tuiCalendarInit();
},
tuiCalendarInit() {
const startContainer = document.getElementById('startpicker-container');
const startTarget = document.getElementById('startpicker');
this.startDatepickerInstance = new TuiDatepicker(startContainer, {
date: new Date(),
language: 'ko',
type: 'month',
input: {
element: startTarget,
format: 'YYYY-MM', //"YYYY-MM-DD" //this.format
},
timePicker: false,
});
this.startDatepickerInstance.on('change', () => this.getStartDt());
},
getStartDt() {
const dt = this.startDatepickerInstance.getDate();
this.targetDateTimeObject.value = String(dt.toISOString()).substr(0, 7);
},
async gridInit() {
const gridHeight01 = this.$refs.gridParent01.offsetHeight - 36;
const gridHeight02 = this.$refs.gridParent02.offsetHeight - 36;
const gridHeight03 = this.$refs.gridParent03.offsetHeight - 36;
const myOptions = {
scrollX: false,
};
this.setGridOption({
gridKey: this.gridName01,
value: Object.assign(
Utility.defaultGridOption(gridHeight01),
myOptions,
),
});
this.setGridOption({
gridKey: this.gridName02,
value: Object.assign(
Utility.defaultGridOption(gridHeight02),
myOptions,
),
});
this.setGridOption({
gridKey: this.gridName03,
value: Object.assign(
Utility.defaultGridOption(gridHeight03),
myOptions,
),
});
var params = this.makeParams();
await this.getGridData01(params);
await this.getGridData02(params);
await this.getGridData03(params);
},
makeParams() {
var params = {};
params['year'] = this.targetDateTimeObject.value.substr(0, 4);
params['month'] = this.targetDateTimeObject.value.substr(5, 2);
params['lang'] = 'ko_KR';
params['blocId'] = this.userInfo.blocId;
params['blocPlcCd'] = 'PLC00002';
return params;
},
async getGridData01(params) {
var res = null;
var columnList = [
{ header: '에너지', name: 'mttNm', align: 'left' },
{
header: '계획 사용량',
name: 'planQty',
align: 'right',
formatter: numberFormatter,
},
{
header: '실적 사용량',
name: 'useQty',
align: 'right',
formatter: numberFormatter,
},
{
header: '전년동월대비 증감률',
name: 'preYyQtyRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '전월대비 증감률',
name: 'preMmQtyRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '비용(원)',
name: 'useCost',
align: 'right',
formatter: numberFormatter,
},
{
header: '전년동월대비 비용 증감률',
name: 'preYyCostRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '전월대비 비용 증감률',
name: 'preMmCostRate',
align: 'right',
formatter: numberFormatter,
},
{
header: 'TOE',
name: 'toe',
align: 'right',
formatter: numberFormatter,
},
{
header: 'tCO2',
name: 'tco2',
align: 'right',
formatter: numberFormatter,
},
];
this.loadGrid01 = false;
res = await this.postApiReturn({
apiKey: 'selectMonthReportFactory',
resKey: 'monthReportFactory',
sendParam: params,
});
this.setGridColumn({
gridKey: this.gridName01,
value: columnList,
});
this.setGridData({
gridKey: this.gridName01,
value: res,
});
this.loadGrid01 = true;
this.setPageData({ isFind: false });
},
async getGridData02(params) {
var res = null;
var columnList = [
{ header: '에너지', name: 'mttNm', align: 'left' },
{ header: '상위공정', name: 'eccNm', align: 'left' },
{
header: '계획 사용량',
name: 'planQty',
align: 'right',
formatter: numberFormatter,
},
{
header: '실적 사용량',
name: 'useQty',
align: 'right',
formatter: numberFormatter,
},
{
header: '전년동월대비 증감률',
name: 'preYyQtyRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '전월대비 증감률',
name: 'preMmQtyRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '비용(원)',
name: 'useCost',
align: 'right',
formatter: numberFormatter,
},
{
header: '전년동월대비 비용 증감률',
name: 'preYyCostRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '전월대비 비용 증감률',
name: 'preMmCostRate',
align: 'right',
formatter: numberFormatter,
},
{
header: 'TOE',
name: 'toe',
align: 'right',
formatter: numberFormatter,
},
{
header: 'tCO2',
name: 'tco2',
align: 'right',
formatter: numberFormatter,
},
];
this.loadGrid02 = false;
res = await this.postApiReturn({
apiKey: 'selectMonthReportEnergy',
resKey: 'monthReportEnergy',
sendParam: params,
});
setRowSpanAttribute(res, 'mttNm');
this.setGridColumn({
gridKey: this.gridName02,
value: columnList,
});
this.setGridData({
gridKey: this.gridName02,
value: res,
});
this.loadGrid02 = true;
this.setPageData({ isFind: false });
},
async getGridData03(params) {
var res = null;
var columnList = [
{ header: '상위공정', name: 'upPlcNm', align: 'left' },
{ header: '공정', name: 'eccNm', align: 'left' },
{ header: '에너지', name: 'mttNm', align: 'left' },
{
header: '계획 사용량',
name: 'planQty',
align: 'right',
formatter: numberFormatter,
},
{
header: '실적 사용량',
name: 'useQty',
align: 'right',
formatter: numberFormatter,
},
{
header: '전년동월대비 증감률',
name: 'preYyQtyRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '전월대비 증감률',
name: 'preMmQtyRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '비용(원)',
name: 'useCost',
align: 'right',
formatter: numberFormatter,
},
{
header: '전년동월대비 비용 증감률',
name: 'preYyCostRate',
align: 'right',
formatter: numberFormatter,
},
{
header: '전월대비 비용 증감률',
name: 'preMmCostRate',
align: 'right',
formatter: numberFormatter,
},
{
header: 'TOE',
name: 'toe',
align: 'right',
formatter: numberFormatter,
},
{
header: 'tCO2',
name: 'tco2',
align: 'right',
formatter: numberFormatter,
},
];
this.loadGrid03 = false;
res = await this.postApiReturn({
apiKey: 'selectMonthReportDept',
resKey: 'monthReportDept',
sendParam: params,
});
setRowSpanAttribute(res, 'upPlcNm');
// setRowSpanAttribute(res, 'mttNm', ['upPlcNm', 'mttNm']);
this.setGridColumn({
gridKey: this.gridName03,
value: columnList,
});
this.setGridData({
gridKey: this.gridName03,
value: res,
});
this.loadGrid03 = true;
this.setPageData({ isFind: false });
},
async search() {
var params = this.makeParams();
await this.getGridData01(params);
await this.getGridData02(params);
await this.getGridData03(params);
},
downloadExcelFile() {
this.makeExcelFile('rowGrid01', '권역 종합.xlsx');
this.makeExcelFile('rowGrid02', '에너지원별 종합.xlsx');
this.makeExcelFile('rowGrid03', '조직별 종합.xlsx');
},
makeExcelFile(gridName, fileName) {
var xlsData = this.makeExcelData(gridName);
var workBook = XLSX.utils.book_new();
var excelData = XLSX.utils.json_to_sheet(xlsData);
var sheetName = fileName;
XLSX.utils.book_append_sheet(workBook, excelData, sheetName);
XLSX.writeFile(workBook, fileName);
},
makeExcelData(gridName) {
var xlsData = null;
var xlsHeader = this.pageData[gridName].column;
var xlsRowData = this.pageData[gridName].data;
var tmpData = [];
var tmpMap = {};
xlsRowData.map(item => {
xlsHeader.map(v => {
// return Object.assign(tmpMap, { [v.header]: item[v.name] || 0 });
var value = item[v.name];
if (value == undefined) {
value = 0;
}
if (typeof value === 'number') {
value = parseFloat(
Utility.setFormatIntDecimal(value, 2).replace(',', ''),
);
}
return Object.assign(tmpMap, { [v.header]: value || 0.0 });
});
tmpData = tmpData.concat(tmpMap);
tmpMap = {};
});
xlsData = tmpData;
return xlsData;
},
},
};
const defaultData = {
isFind: false,
rowGrid01: {
data: [],
option: {},
column: [],
},
rowGrid02: {
data: [],
option: {},
column: [],
},
rowGrid03: {
data: [],
option: {},
column: [],
},
};
function setRowSpanAttribute(res, targetAttributeName, targetAttributeList) {
if (!(res.length && res.length >= 2)) {
return;
}
if (targetAttributeList == undefined) {
var valueList = [];
var rowSpanValueList = [];
var currentIdx = 0;
for (var i = 0; i < res.length; i++) {
valueList.push(res[i][targetAttributeName]);
}
rowSpanValueList[0] = [valueList[0], 1, currentIdx];
for (var i = 1; i < valueList.length; i++) {
currentIdx += 1;
if (valueList[i] === rowSpanValueList[rowSpanValueList.length - 1][0]) {
rowSpanValueList[rowSpanValueList.length - 1][1] += 1;
} else {
rowSpanValueList[rowSpanValueList.length] = [
valueList[i],
1,
currentIdx,
];
}
}
for (var i = 0; i < rowSpanValueList.length; i++) {
if (rowSpanValueList[i][1] === 1) {
continue;
}
res[rowSpanValueList[i][2]]['_attributes'] = { rowSpan: new Object() };
res[rowSpanValueList[i][2]]['_attributes']['rowSpan'][
targetAttributeName
] = rowSpanValueList[i][1];
}
} else {
// targetAttributeList 전달 ...
var valueList = [];
var rowSpanValueList = [];
var currentIdx = 0;
for (var i = 0; i < res.length; i++) {
var tempList = [];
for (var j = 0; j < targetAttributeList.length; j++) {
tempList.push(res[i][targetAttributeList[j]]);
}
valueList.push(tempList);
}
rowSpanValueList[0] = [valueList[0], 1, currentIdx];
for (var i = 1; i < valueList.length; i++) {
currentIdx += 1;
if (
comepareValueWithTargetAttributeList(
valueList[i],
rowSpanValueList[rowSpanValueList.length - 1][0],
)
) {
rowSpanValueList[rowSpanValueList.length - 1][1] += 1;
} else {
rowSpanValueList[rowSpanValueList.length] = [
valueList[i],
1,
currentIdx,
];
}
}
for (var i = 0; i < rowSpanValueList.length; i++) {
if (rowSpanValueList[i][1] === 1) {
continue;
}
res[rowSpanValueList[i][2]]['_attributes'] = { rowSpan: new Object() };
res[rowSpanValueList[i][2]]['_attributes']['rowSpan'][
targetAttributeName
] = rowSpanValueList[i][1];
}
}
function comepareValueWithTargetAttributeList(valueList_01, valueList_02) {
for (var i = 0; i < valueList_01.length; i++) {
if (valueList_01[i] != valueList_02[i]) {
return false;
}
}
return true;
}
}
function setRowSpanAttribute_old(
res,
targetAttributeName,
targetAttributeList,
) {
if (!(res.length && res.length >= 2)) {
return;
}
if (targetAttributeList == undefined) {
var currentCnt = 1;
var currentIdx = 0;
var currentValue = res[0][targetAttributeName];
for (var i = 1; i < res.length - 1; i++) {
if (res[i][targetAttributeName] != currentValue) {
if (res[currentIdx]['_attributes'] == undefined) {
res[currentIdx]['_attributes'] = { rowSpan: new Object() };
}
res[currentIdx]['_attributes']['rowSpan'][
targetAttributeName
] = currentCnt;
currentValue = res[i][targetAttributeName];
currentCnt = 1;
currentIdx = i;
} else if (
res[i][targetAttributeName] == currentValue &&
i == res.length - 2
) {
currentCnt = currentCnt + 1;
if (res[currentIdx]['_attributes'] == undefined) {
res[currentIdx]['_attributes'] = { rowSpan: new Object() };
}
res[currentIdx]['_attributes']['rowSpan'][
targetAttributeName
] = currentCnt;
} else {
currentCnt = currentCnt + 1;
}
}
} else {
var currentCnt = 1;
var currentIdx = 0;
for (var i = 1; i < res.length - 1; i++) {
if (!compareValue(res, targetAttributeList, currentIdx, i)) {
if (res[currentIdx]['_attributes'] == undefined) {
res[currentIdx]['_attributes'] = { rowSpan: new Object() };
}
if (currentCnt != 1) {
res[currentIdx]['_attributes']['rowSpan'][
targetAttributeName
] = currentCnt;
}
currentCnt = 1;
currentIdx = i;
} else if (
compareValue(res, targetAttributeList, currentIdx, i) &&
i == res.length - 2
) {
currentCnt = currentCnt + 1;
if (res[currentIdx]['_attributes'] == undefined) {
res[currentIdx]['_attributes'] = { rowSpan: new Object() };
}
if (currentCnt != 1) {
res[currentIdx]['_attributes']['rowSpan'][
targetAttributeName
] = currentCnt;
}
} else {
currentCnt = currentCnt + 1;
}
}
}
function compareValue(res, targetAttributeList, currentIdx, targetIdx) {
for (var i = 0; i < targetAttributeList.length; i++) {
if (
res[currentIdx][targetAttributeList[i]] !=
res[targetIdx][targetAttributeList[i]]
) {
return false;
}
}
return true;
}
}
function numberFormatter({ value }) {
return Utility.setFormatIntDecimal(value, 2);
}
</script>
<style lang="scss" scoped>
#startpicker-container,
#endpicker-container {
position: relative;
z-index: 20;
}
.v-input__custom {
flex: 0 0 auto;
&.half {
width: calc(50% - 20px);
}
}
::v-deep {
.tui-timepicker-row {
display: flex;
justify-content: space-around;
background-color: #edf4fc;
.tui-timepicker-column.tui-timepicker-colon {
color: #000 !important;
}
}
}
</style>