Files
SaaS-lib/hx-ai-intelligent/src/view/carbonEmissionManage/carbonInventoryCheck/fillInPage/index copy.vue
fks-xuxinyue 43fcd2b161 修改优化
2024-08-20 15:40:08 +08:00

1633 lines
49 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="main">
<div class="left">
<div class="top">
<a-form style="width: 100%; margin: 0 auto">
<div class="ns-form-title">
<div class="title">{{ props.year }}年济阳站碳盘查报告</div>
<div class="standard">
<span>适用标准ISO 14064-1</span>
<a-button type="primary" style="margin-left: 12px" @click="openCategoryConfig"
>类别配置</a-button
>
</div>
</div>
<div style="padding: 0 16px !important; width: 100%">
<a-row>
<a-col :span="24" style="margin-bottom: 16px">
<a-input-search
v-model:value="searchValue"
style="margin-bottom: 8px"
placeholder="请输入关键词" />
</a-col>
</a-row>
</div>
</a-form>
<a-tree
:expanded-keys="expandedKeysL"
:selectedKeys="selectedKeysL"
:auto-expand-parent="autoExpandParent"
:tree-data="gData"
v-if="gData && gData.length > 0"
:height="233"
show-line
style="padding: 0 16px !important"
@expand="onExpandL"
@select="onSelectL"
block-node>
<template #title="{ cnValue }">
<span v-if="cnValue && searchValue && cnValue.indexOf(searchValue) > -1">
{{ cnValue.substring(0, cnValue.indexOf(searchValue)) }}
<span style="color: #f50">{{ searchValue }}</span>
{{ cnValue.substring(cnValue.indexOf(searchValue) + searchValue.length) }}
</span>
<span v-else>{{ cnValue }}</span>
</template>
</a-tree>
</div>
<!-- <div class="bottom">
<a-form style="width: 100%; margin: 0 auto">
<div class="ns-form-title"><div class="title">报告相关</div></div>
<div class="button" style="padding: 0 16px !important">
<div :class="{ pftj: isClickedPftj }" @click="showPftj">排放统计</div>
<div :class="{ tplx: isClickedTplx }" @click="showTplx">碳排流向</div>
</div>
</a-form>
</div> -->
</div>
<div class="right">
<a-button
type="primary"
@click="changeParentData"
style="position: absolute; right: 25px; top: 10px; z-index: 99">
返回
</a-button>
<a-tabs v-model:activeKey="activeKey" @change="handleTabChange" style="height: 100%">
<a-tab-pane key="1" tab="排放源">
<div style="display: flex; height: 100%">
<div class="mainLeft">
<a-tree
:expandedKeys="expandedKeysR"
:selectedKeys="selectedKeysR"
:checkedKeys="checkedKeys"
:tree-data="treeData"
@select="onSelectR"
block-node>
<template #title="data">
<div class="treeRow">
<div>
<span>{{ data.emissionSource }}</span>
</div>
<div class="actionMore">
<EditOutlined @click="editUnit(data)" />
<MinusCircleOutlined style="margin-left: 6px" @click="delUnit(data)" />
</div>
</div>
</template>
</a-tree>
<div class="addTreeNode">
<a-button type="primary" style="width: 100%" @click="addTreeNodeData"
>新增</a-button
>
</div>
</div>
<div class="mainRight">
<a-table
:columns="columns"
:data-source="data"
:pagination="false"
bordered
size="middle"
:scroll="{ y: 500 }">
<template #title>
<a-button type="primary" @click="downLoadVoucher">凭证</a-button>
</template>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'dataSources'">
<span v-if="record.dataSources">{{ record.dataSources.label }}</span>
</template>
<template v-if="column.dataIndex === 'carbonSource'">
<span v-if="record.carbonSource">{{ record.carbonSource.label }}</span>
</template>
<template v-if="column.key === 'action'">
<span>
<a @click="edit(record)">编辑</a>
</span>
</template>
</template>
</a-table>
</div>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="排放统计">
<a-table
:columns="pftjColumn"
:data-source="pftjData"
bordered
:pagination="false"
:scroll="{ x: 2000, y: 500 }">
<template #bodyCell="{ column, text, record }">
{{ text || '-' }}
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="3" tab="碳排流向">
<div ref="tplxChart" style="width: 100%; height: 68vh"></div>
</a-tab-pane>
</a-tabs>
</div>
<!-- 类别配置 -->
<a-drawer
:width="500"
:visible="categoryConfig"
v-if="categoryConfig"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
title="类别配置"
@close="closeCategoryConfig">
<div class="search">
<a-input-search
v-model:value="searchcategoryConfig"
placeholder="请输入类别名称"
style="width: 250px"
@search="onSearch" />
</div>
<div class="treePart">
<a-tree
:expanded-keys="categoryExpandedKeys"
:selectedKeys="categorySelectedKeys"
:checkedKeys="categoryCheckedKeys"
:tree-data="categoryTreeData"
v-if="categoryTreeData && categoryTreeData.length > 0"
class="draggable-tree"
@check="checkTreeNode"
checkable
block-node>
<template #title="data">
<div class="treeRow">
<div>
<span>{{ data.cnValue }}</span>
</div>
</div>
</template>
</a-tree>
</div>
<template #footer>
<a-button style="margin-right: 8px" @click="closeCategoryConfig">取消</a-button>
<a-button type="primary" @click="submitCategory">确定</a-button>
</template>
</a-drawer>
<!-- 新增/编辑排放源树节点 -->
<a-drawer
:width="500"
:visible="addTreeNodeVisible"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
@close="onClose">
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol">
<a-form-item ref="name" label="排放源" name="emissionSource">
<a-input v-model:value="formState.emissionSource" placeholder="请输入排放源" />
</a-form-item>
<a-form-item ref="name" label="计量单位" name="unitId">
<a-cascader v-model:value="formState.unitId" :options="measurementUnit" />
</a-form-item>
<a-form-item ref="name" label="温室气体" name="greenhouseGases">
<a-select ref="select" v-model:value="formState.greenhouseGases">
<a-select-option v-for="(item, index) in groupData" :key="index" :value="item.id">
{{ item.cnValue }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="name" label="碳排单位" name="carbonEmissionUnits">
<a-cascader v-model:value="formState.carbonEmissionUnits" :options="CarbonUnit" />
</a-form-item>
<!-- <a-form-item label="排放类型" name="emissionType" :required="isRequired">
<a-select v-model:value="formState.emissionType" placeholder="请选择排放类型">
<a-select-option v-for="(item, index) in emissionTypeDic" :key="index" :value="item.id">
{{ item.cnValue }}
</a-select-option>
</a-select>
</a-form-item> -->
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose">取消</a-button>
<a-button type="primary" @click="onSubmit">确定</a-button>
</template>
</a-drawer>
<!-- 点击凭证弹出框 -->
<a-drawer
:width="500"
:visible="openVoucher"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
@close="onCloseVoucher">
<a-table
:columns="voucherColumns"
:data-source="voucherData"
:rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectionChange }"
rowKey="id"
:pagination="false">
<template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'name'">
<a>{{ text }}</a>
</template>
</template>
</a-table>
<template #footer>
<a-button style="margin-right: 8px" @click="onCloseVoucher">取消</a-button>
<a-button type="primary" @click="downLoadMore">批量下载</a-button>
</template>
</a-drawer>
<!-- 点击编辑弹出框 -->
<a-drawer
:width="500"
:visible="editData"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
@close="onCloseEditData">
<a-form
ref="editFormRef"
:model="editFormState"
:label-col="labelCol"
:wrapper-col="wrapperCol">
<a-row>
<a-col :span="24">
<a-form-item ref="name" label="数据来源" name="dataSources">
<a-select
ref="select"
v-model:value="editFormState.dataSources"
@change="changeSelect">
<a-select-option value="1">自行推估</a-select-option>
<a-select-option value="2">定期量测</a-select-option>
<a-select-option value="3">自动测量</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item ref="name" label="消耗量" name="consumption">
<ns-input v-model:value="editFormState.consumption" :disabled="canEdit" />
</a-form-item>
</a-col>
</a-row>
<a-row v-if="automatic">
<a-col :span="24">
<a-form-item label="能耗类型">
<a-select
v-model:value="editFormState.energyConsumptionType"
@change="changeEnergyType"
placeholder="请选择能耗类型">
<a-select-option
v-for="(item, index) in energyTypeOptions"
:key="index"
:value="item.dicKey">
{{ item.cnValue }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item ref="name" label="采集节点" name="collectionNode">
<a-tree-select
v-model:value="editFormState.collectionNode"
:tree-line="true"
@select="selectNode"
:tree-data="collectingNodes">
</a-tree-select>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<a-form-item ref="name" label="因子值" name="factorId">
<ns-input v-model:value="editFormState.factorId" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item ref="name" label="关键字" name="emissionFactors">
<a-input-search
v-model:value="editFormState.emissionFactors"
@search="searchKey"
placeholder="请输入关键字" />
</a-form-item>
</a-col>
</a-row>
</a-form>
<a-table
:columns="drawerColumns"
:data-source="newTableData"
bordered
rowKey="id"
:scroll="{ y: 300 }"
size="small"
style="margin-bottom: 10px"
:rowSelection="{
selectedRowKeys: selectedRowKeysEdit,
onChange: onSelectionChangeEdit,
type: 'radio',
}"
:pagination="false">
</a-table>
<a-pagination
:current="queryData.pageNum"
:total="total"
:page-size="queryData.pageSize"
:pageSizeOptions="['5']"
style="display: flex; justify-content: center; margin-top: 16px"
:show-size-changer="true"
:show-quick-jumper="true"
@change="onChange" />
<a-upload-dragger
v-model:fileList="fileList"
accept=".pdf"
name="file"
@remove="handleFileRemove"
:before-upload="beforeUpload"
@change="handleChange">
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-hint">1.仅支持pdf格式文件或文件夹</p>
<p class="ant-upload-hint">2.文件名命名规则为能源种类_年份</p>
<p class="ant-upload-hint">3.每次上传自动覆盖</p>
</a-upload-dragger>
<template #footer>
<a-button style="margin-right: 8px" @click="onCloseEditData">取消</a-button>
<a-button type="primary" @click="submitEditData">确定</a-button>
</template>
</a-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, toRaw, defineEmits, nextTick } from 'vue';
import { http } from '/nerv-lib/util/http';
import { Pagination, Modal, message, Upload } from 'ant-design-vue';
import type { TreeProps, UploadChangeParam } from 'ant-design-vue';
import {
EditOutlined,
PlusCircleOutlined,
MinusCircleOutlined,
UploadOutlined,
InboxOutlined,
} from '@ant-design/icons-vue';
import * as echarts from 'echarts';
import { voucherColumns, drawerColumns } from '../config';
import {
energyConsumption,
carbonEmissionFactorLibrary,
carbonInventoryCheck,
uploadPic,
} from '/@/api/carbonEmissionFactorLibrary';
import { group } from '/@/api/deviceManage';
import { debug, log } from 'node:console';
import { dict } from '/@/api';
defineOptions({
energyType: 'fillInPage', // 与页面路由name一致缓存才可生效
components: {
'a-pagination': Pagination,
},
});
// 父组件id和年份
const props = defineProps({
reportId: {
type: Number,
},
year: {
type: String,
},
startTime: {
type: String,
},
endTime: {
type: String,
},
});
const activeKey = ref('1');
const orgId = ref('');
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
orgId.value = result;
const fetch = (api, params = { orgId: orgId.value }, config) => {
return http.post(api, params, config);
};
// 左侧树结构
const x = 3;
const y = 2;
const z = 1;
const genData: TreeProps['treeData'] = [];
const generateData = (_level: number, _preKey?: string, _tns?: TreeProps['treeData']) => {
const preKey = _preKey || '0';
const tns = _tns || genData;
const children = [];
for (let i = 0; i < x; i++) {
const key = `${preKey}-${i}`;
tns.push({ title: key, key });
if (i < y) {
children.push(key);
}
}
if (_level < 0) {
return tns;
}
const level = _level - 1;
children.forEach((key, index) => {
tns[index].children = [];
return generateData(level, key, tns[index].children);
});
};
generateData(z);
const dataList: TreeProps['treeData'] = [];
const generateList = (data: TreeProps['treeData']) => {
for (let i = 0; i < data.length; i++) {
const node = data[i];
const key = node.key;
dataList.push({ key, title: key });
if (node.children) {
generateList(node.children);
}
}
};
generateList(genData);
const getParentKey = (
key: string | number,
tree: TreeProps['treeData'],
): string | number | undefined => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
const expandedKeysL = ref<(string | number)[]>(['0-0']);
const selectedKeysL = ref<string[]>(['0-0-0']);
const searchValue = ref<string>('');
const autoExpandParent = ref<boolean>(true);
const gData = ref<TreeProps['treeData']>(genData);
const onExpandL = (keys: string[]) => {
expandedKeysL.value = keys;
autoExpandParent.value = false;
};
watch(searchValue, (value) => {
const expanded = dataList
.map((item: TreeProps['treeData'][number]) => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, gData.value);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
expandedKeysL.value = expanded;
searchValue.value = value;
autoExpandParent.value = true;
});
// 被选中的树节点
const categoryId = ref();
const onSelectL = (selectedKey: string[], info: any) => {
selectedKeysL.value = selectedKey;
categoryId.value = info.node.conId;
formState.value.categoryId = categoryId.value;
if (fillInPage.value) {
getEmissionSourceTree();
} else if (isClickedPftj.value) {
queryEmissionData.value.categoryId = info.node.conId;
getEmissionStatistic();
} else if (isClickedTplx.value) {
queryFlowDirection.value.categoryId = info.node.conId;
getCarbonFlowDirection();
}
};
// 获取碳盘查报告树
const selectedIds = ref([]);
const getReportTree = () => {
fetch(carbonInventoryCheck.getCategoryTree, {
reportId: props.reportId,
orgId: orgId.value,
}).then((res) => {
gData.value = res.data;
if (res.data.length > 0) {
categoryId.value = res.data[0].children[0].conId;
formState.value.categoryId = categoryId.value;
queryEmissionData.value.categoryId = categoryId.value;
queryFlowDirection.value.categoryId = categoryId.value;
selectedIds.value = getAllIds(gData.value);
}
getEmissionSourceTree();
});
};
getReportTree();
const getAllIds = (nodes: TreeNode[]): number[] => {
return nodes.flatMap((node) => [node.id, ...(node.children ? getAllIds(node.children) : [])]);
};
// 右侧树数据
const treeData = ref<TreeProps['treeData']>(genData);
// 获取数据
const getTableId = ref();
const changeSelectedKeys = ref(false);
const getEmissionSourceTree = () => {
fetch(carbonInventoryCheck.getInventoryTree, { categoryId: categoryId.value }).then((res) => {
treeData.value = res.data;
if (changeSelectedKeys.value) {
selectedKeysR.value = [`0-${treeData.value.length - 1}`];
changeSelectedKeys.value = false;
getTableId.value = res.data[res.data.length - 1].id;
if (res.data.length != 0) {
getPFYTableList(getTableId.value);
getTableHeardUnit(getTableId.value);
} else {
data.value = [];
columns.value[1].title = '消耗量';
columns.value[2].title = '碳排因子';
columns.value[3].title = '排放量';
}
} else {
selectedKeysR.value = ['0-0'];
getTableId.value = res.data[0].id;
if (res.data.length != 0) {
getPFYTableList(getTableId.value);
getTableHeardUnit(getTableId.value);
} else {
data.value = [];
columns.value[1].title = '消耗量';
columns.value[2].title = '碳排因子';
columns.value[3].title = '排放量';
}
}
});
};
// 排放源树中被选中的树节点
const onSelectR = (selectedKey: string[], info: any) => {
selectedKeysR.value = selectedKey;
getTableId.value = info.selectedNodes[0].id;
getPFYTableList(getTableId.value);
getTableHeardUnit(getTableId.value);
};
// 获取表头单位
const getTableHeardUnit = (id) => {
fetch(carbonInventoryCheck.findUnitById, { id: id }).then((res) => {
if (res.data) {
columns.value[1].title = '消耗量【' + res.data.unit + '】';
columns.value[2].title = '碳排因子【' + res.data.carbonEmission + '】';
columns.value[3].title = '排放量【' + res.data.carbonUnits + '】';
}
});
};
const expandedKeysR = ref<string[]>([]);
const selectedKeysR = ref<string[]>(['0-0']);
const checkedKeys = ref<string[]>([]);
watch(expandedKeysR, () => {
console.log('expandedKeysR', expandedKeysR);
});
watch(selectedKeysR, () => {
console.log('selectedKeys', selectedKeysR);
});
watch(checkedKeys, () => {
console.log('checkedKeys', checkedKeys);
});
// 类别配置
const categoryConfig = ref(false);
const searchcategoryConfig = ref();
const categoryTreeData = ref<TreeProps['treeData']>(genData);
const categoryExpandedKeys = ref<string[]>();
const categorySelectedKeys = ref<string[]>([]);
const categoryCheckedKeys = ref<string[]>([]);
const checkTreeNode = (checkedKeys, info) => {
categoryCheckedKeys.value = checkedKeys;
queryConfigure.value.dictionaryIdList = categoryCheckedKeys.value;
};
const getUnitTree = () => {
fetch(carbonEmissionFactorLibrary.dictionaryUnitManagement, {
grp: 'CATEGORY_CONFIGURATION',
}).then((res) => {
categoryTreeData.value = res.data;
categoryCheckedKeys.value = selectedIds.value.map((id) => String(id));
// 格式化树节点数据并设置 key
categoryTreeData.value = formatTreeData(categoryTreeData.value);
});
};
const formatTreeData = (treeData) => {
return treeData.map((node) => {
const newNode = { ...node };
newNode.key = String(node.id); // 将 id 转换为字符串作为 key
if (node.children) {
newNode.children = formatTreeData(node.children);
}
return newNode;
});
};
const openCategoryConfig = () => {
categoryConfig.value = true;
getUnitTree();
};
// 类别配置树点击确定
const queryConfigure = ref({
orgId: orgId.value,
reportId: props.reportId,
});
const submitCategory = () => {
fetch(carbonInventoryCheck.updateCategoryTree, queryConfigure.value).then((res) => {
categoryConfig.value = false;
getReportTree();
});
};
const closeCategoryConfig = () => {
categoryConfig.value = false;
};
// 新增/编辑排放源树节点
const addTreeNodeVisible = ref(false);
const formRef = ref();
const labelCol = { span: 5 };
const wrapperCol = { span: 19 };
const formState = ref({
orgId: orgId.value,
year: props.year,
startTime: props.startTime,
endTime: props.endTime,
});
// 计量单位的变量
const measurementUnit = ref([]);
// 碳排单位变量
const CarbonUnit = ref([]);
// 温室气体变量
const groupData = ref([]);
// 定义form表单的必填
const rules: Record<string, Rule[]> = {
emissionSource: [{ required: true, message: '请输入排放源', trigger: 'change' }],
unitId: [{ required: true, message: '请选择计量单位', trigger: 'change' }],
greenhouseGases: [{ required: true, message: '请选择温室气体', trigger: 'change' }],
carbonEmissionUnits: [{ required: true, message: '请选择碳排单位', trigger: 'change' }],
};
const addTreeNodeData = () => {
addTreeNodeVisible.value = true;
getDictData();
};
// 获取字典值
const getDictData = () => {
// 获取计量单位的数据
fetch(carbonEmissionFactorLibrary.dictionaryUnitManagement, { grp: 'MEASUREMENT_UNIT' }).then(
(res) => {
measurementUnit.value = res.data;
measurementUnit.value = measurementUnit.value.map((item) => ({
value: item.id,
label: item.cnValue,
children: item.children
? item.children.map((child) => ({
value: child.id,
label: child.cnValue,
}))
: [],
}));
},
);
// 获取碳排单位的数据
fetch(carbonEmissionFactorLibrary.dictionaryUnitManagement, { grp: 'GREENHOUSE_GASES' }).then(
(res) => {
CarbonUnit.value = res.data;
CarbonUnit.value = CarbonUnit.value.map((item) => ({
value: item.id,
label: item.cnValue,
children: item.children
? item.children.map((child) => ({
value: child.id,
label: child.cnValue,
}))
: [],
}));
},
);
// 获取温室气体数据
fetch(carbonEmissionFactorLibrary.findOutermost, { grp: 'GREENHOUSE_GASES' }).then((res) => {
groupData.value = res.data;
});
};
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
console.log('values', formState, toRaw(formState));
if (formState.value.unitId) {
formState.value.unitId = formState.value.unitId.join(',').split(',')[1];
}
if (formState.value.carbonEmissionUnits) {
formState.value.carbonEmissionUnits = formState.value.carbonEmissionUnits
.join(',')
.split(',')[1];
}
if (formState.value.id) {
fetch(carbonInventoryCheck.update, formState.value).then((res) => {
addTreeNodeVisible.value = false;
formState.value = {
orgId: orgId.value,
};
message.success('操作成功!');
getEmissionSourceTree();
});
} else {
fetch(carbonInventoryCheck.create, formState.value).then((res) => {
addTreeNodeVisible.value = false;
formState.value = {
orgId: orgId.value,
};
message.success('操作成功!');
changeSelectedKeys.value = true;
getEmissionSourceTree();
getPFYTableList(res.data.id);
});
}
})
.catch((error) => {
console.log('error', error);
});
};
// 填报表格数据
const data = ref([]);
// 填报页表头
const columns = ref([
{
title: '日期',
dataIndex: 'acquisitionDate',
key: 'acquisitionDate',
},
{
title: '消耗量',
children: [
{
title: '数据来源',
dataIndex: 'dataSources',
key: 'dataSources',
},
{
title: '数值',
dataIndex: 'consumption',
key: 'consumption',
},
],
},
{
title: '碳排因子',
children: [
{
title: '数据来源',
dataIndex: 'carbonSource',
key: 'carbonSource',
},
{
title: '数值',
dataIndex: 'emissionFactors',
key: 'emissionFactors',
},
],
},
{
title: '排放量',
dataIndex: 'emissions',
key: 'emissions',
fixed: 'right',
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
width: 80,
fixed: 'right',
},
]);
// 获取table数据
const getPFYTableList = (id) => {
fetch(carbonInventoryCheck.getDetailsList, { inventoryId: id }).then((res) => {
data.value = res.data;
});
};
const editUnit = (data) => {
getDictData();
addTreeNodeVisible.value = true;
setTimeout(() => {
let selectDevice = ref([Number(data.unitId)]);
findParentIds(measurementUnit.value, data.unitId, selectDevice.value);
data.unitId = selectDevice;
let selectDeviceC = ref([Number(data.carbonEmissionUnits)]);
findParentIds(CarbonUnit.value, data.carbonEmissionUnits, selectDeviceC.value);
data.carbonEmissionUnits = selectDeviceC;
formState.value.id = data.id;
formState.value = data;
}, 500);
};
// 定义一个递归函数来查找每一级的id 设备类型回显 层级方法
function findParentIds(tree: any, targetId: number, result: any) {
for (let item of tree) {
if (item.children && item.children.length > 0) {
if (item.children.some((child: any) => child.value === targetId)) {
result.unshift(item.value); // 将当前节点的id添加到结果数组的最前面
findParentIds(tree, item.value, result); // 递归查找父级节点的id
break; // 找到后可以退出循环
}
}
}
}
// 删除排放源左侧树节点
const delUnit = (data) => {
Modal.confirm({
title: '警告',
content: '确定要删除吗?',
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
fetch(carbonInventoryCheck.del, { id: data.id }).then((res) => {
message.success('操作成功!');
getEmissionSourceTree();
});
},
onCancel() {
console.log('Cancel');
},
});
};
const onClose = () => {
addTreeNodeVisible.value = false;
formState.value = {
orgId: orgId.value,
};
formRef.value.resetFields();
};
// 点击凭证弹出框
const openVoucher = ref(false);
const downLoadVoucher = () => {
fetch(carbonInventoryCheck.voucherDownloadList, { bizType: 2 }).then((res) => {
voucherData.value = res.data;
});
openVoucher.value = true;
};
const selectedRowKeys = ref([]);
const onSelectionChange = (selectedKeys, selectedRows) => {
selectedRowKeys.value = selectedKeys;
};
const voucherData = ref([]);
const downLoadMore = () => {
const deleteIds = ref(new FormData());
selectedRowKeys.value.forEach((item) => {
deleteIds.value.append('ids', item);
});
const config = {
responseType: 'blob',
};
fetch(uploadPic.downloadZip, deleteIds.value, config)
.then((res) => {
// 创建一个 URL 对象,指向图片数据的 blob
const url = window.URL.createObjectURL(new Blob([res]));
// 创建一个 <a> 标签,用于触发下载
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', '碳盘查凭证.zip'); // 设置下载的文件名
document.body.appendChild(link);
link.click();
// 清理 URL 对象
window.URL.revokeObjectURL(url);
onCloseVoucher();
})
.catch((error) => {
console.error('下载图片失败:', error);
});
};
const onCloseVoucher = () => {
selectedRowKeys.value = [];
openVoucher.value = false;
};
// 点击编辑弹出框
const editData = ref(false);
const editFormRef = ref();
const editFormState = ref({
orgId: orgId.value,
});
const newTableData = ref([]);
const total = ref<number>();
const selectedRowKeysEdit = ref([]);
const onSelectionChangeEdit = (selectedKeys, selectedRows) => {
selectedRowKeysEdit.value = selectedKeys;
editFormState.value.factorId = selectedKeys;
};
const queryParams = ref({
pageNum: 1,
pageSize: 10,
orgId: orgId.value,
});
const queryData = ref({
orgId: orgId.value,
pageNum: 1,
pageSize: 5,
});
const energyTypeOptions = ref([]);
const acquisitionDate = ref();
const edit = async (record) => {
acquisitionDate.value = record.acquisitionDate;
// 获取能耗类型
const options = await dict({ params: { dicKey: 'ENERGY_TYPE' } });
energyTypeOptions.value = options.data.data;
getNewTable();
editFormState.value.id = record.id;
editFormState.value.dataSources = record.dataSources;
editFormState.value.consumption = record.emissions;
editFormState.value.collectionNode = record.carbonSource;
editFormState.value.factorId = record.factorId;
selectedRowKeysEdit.value = [record.factorId];
fetch(uploadPic.select, { bizId: record.id, bizType: 2 }).then((res) => {
fileList.value = res.data.map((item) => ({
uid: item.id.toString(), // 使用文件的id作为唯一标识
name: item.fileName, // 文件名
status: 'done', // 设置默认状态为已完成
type: 'done',
url: item.filePath, // 文件的URL这里假设用示例的URL格式
}));
});
editData.value = true;
};
const getNewTable = () => {
fetch(carbonEmissionFactorLibrary.getTableList, queryData.value).then((res) => {
newTableData.value = res.data.records;
total.value = res.data.total;
});
};
const searchKey = () => {
queryData.value.emissionSources = editFormState.value.emissionFactors;
getNewTable();
};
// 数据来源选择框改变
const canEdit = ref(false);
const automatic = ref(false);
const changeSelect = (value) => {
if (value === '3') {
canEdit.value = true;
automatic.value = true;
} else {
automatic.value = false;
}
};
// 定义采集节点数的变量
const collectingNodes = ref<TreeSelectProps['treeData']>([]);
const getTypeConsume = ref();
const changeEnergyType = (value) => {
getTypeConsume.value = value;
// 获取自动采集节点的数据
fetch(group.queryDeviceGroupTree, { energyType: value, orgId: orgId.value }).then((res) => {
collectingNodes.value = res.data;
collectingNodes.value = collectingNodes.value.map((item) => ({
value: item.id,
label: item.pointName,
children: item.children
? item.children.map((child) => ({
value: child.id,
label: child.pointName,
}))
: [],
}));
});
};
const selectNode = (value) => {
const getConsumeData = ref({
acquisitionDate: acquisitionDate.value,
collectionNode: value,
orgId: orgId.value,
energyConsumptionType: getTypeConsume.value,
});
fetch(carbonInventoryCheck.nodeCancellationConsumption, getConsumeData.value).then((res) => {
editFormState.value.consumption = res.data;
});
};
// 上传附件
const fileList = ref<UploadProps['fileList']>([]);
const isValidFileName = (filename: string): boolean => {
const regex = /^[\s\S]+_\d{4}\.pdf$/;
return regex.test(filename);
};
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
const filename = file.name;
if (!isValidFileName(filename)) {
message.error('文件名不符合规则');
return Upload.LIST_IGNORE; // 阻止文件上传
}
return false;
};
const handleChange = (info: UploadChangeParam) => {
// fileList.value = [...info.fileList];
const { fileList: newFileList } = info;
// 删除fileList中的第一条数据
if (newFileList.length > 1) {
delIds.value.push(info.fileList[0].uid);
newFileList.shift(); // 删除第一条数据
}
// 更新fileList
fileList.value = [...newFileList];
};
const delIds = ref([]);
const handleFileRemove = (file) => {
delIds.value.push(file.uid);
const newFileList = [];
fileList.value.forEach((item) => {
if (item.uid !== file.uid) {
newFileList.push(item);
}
});
fileList.value = newFileList;
};
// 数据提交;
const submitEditData = () => {
editFormRef.value
.validate()
.then(() => {
console.log('values', editFormState, toRaw(editFormState));
if (editFormState.value.factorId.length > 0) {
editFormState.value.factorId = editFormState.value.factorId.join(',');
}
if (editFormState.value.dataSources.value) {
editFormState.value.dataSources = editFormState.value.dataSources.value;
}
if (editFormState.value.collectionNode) {
editFormState.value.collectionNode = editFormState.value.collectionNode.value;
}
fetch(carbonInventoryCheck.updateTable, editFormState.value).then((res) => {
if (fileList.value.length !== 0) {
const formData = ref(new FormData());
fileList.value.forEach((file) => {
if (file.type !== 'done') {
formData.value.append('files', file.originFileObj);
}
});
formData.value.append('bizType', 2);
formData.value.append('bizId', editFormState.value.id);
delIds.value.forEach((item) => {
formData.value.append('deleteList', item);
});
fetch(uploadPic.uploadfiles, formData.value)
.then((res) => {
message.success('操作成功!');
editData.value = false;
delIds.value = [];
getPFYTableList(getTableId.value);
})
.catch((error) => {
console.log('error', error);
});
} else {
editData.value = false;
delIds.value = [];
getPFYTableList(getTableId.value);
}
});
})
.catch((error) => {
console.log('error', error);
});
};
const onCloseEditData = () => {
editData.value = false;
delIds.value = [];
editFormState.value = {
orgId: orgId.value,
};
selectedRowKeysEdit.value = [];
canEdit.value = false;
automatic.value = false;
};
// 分页器
const onChange = (pageNumber: number, size: number) => {
queryData.value.pageNum = pageNumber;
queryData.value.pageSize = size;
getNewTable();
};
// 点击切换排放统计/碳排流向
const fillInPage = ref(true);
const isClickedPftj = ref(false);
const isClickedTplx = ref(false);
const pftjData = ref([]);
const showPftj = () => {
fillInPage.value = false;
isClickedPftj.value = true;
isClickedTplx.value = false;
getEmissionStatistic();
};
const showTplx = () => {
fillInPage.value = false;
isClickedPftj.value = false;
isClickedTplx.value = true;
getCarbonFlowDirection();
};
// 获取碳排流向数据
const queryFlowDirection = ref({
orgId: orgId.value,
reportId: props.reportId,
year: props.year,
categoryId: categoryId.value,
});
const linksData = ref([]);
const datalist = ref([]);
const getCarbonFlowDirection = () => {
fetch(carbonInventoryCheck.carbonFlowDirection, queryFlowDirection.value).then((res) => {
console.log(res);
linksData.value = res.data[0];
datalist.value = res.data[1];
drawEcharts();
});
};
// 获取排放统计数据
const queryEmissionData = ref({
orgId: orgId.value,
reportId: props.reportId,
year: props.year,
categoryId: categoryId.value,
});
const tableHeader = ref([]);
const getEmissionStatistic = () => {
fetch(carbonInventoryCheck.emissionStatistic, queryEmissionData.value).then((res) => {
pftjData.value = res.data.body;
// 将数据表头数据按顺序组织
const orderedKeys = res.data.title.map((obj) => Object.keys(obj)[0]); // 获取原始键的顺序
const result: { [key: string]: string } = {};
res.data.title.forEach((obj) => {
const key = Object.keys(obj)[0];
result[key] = obj[key];
});
// 确保按照原始顺序返回结果
const orderedResult: { [key: string]: string } = {};
orderedKeys.forEach((key) => {
if (result[key] !== undefined) {
orderedResult[key] = result[key];
}
});
const data = orderedResult;
// 将数据转换为 DataItem 数组
tableHeader.value = [];
pftjColumn.value = [
{
title: '排放分类',
dataIndex: 'emissionClassify',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
const rowSpan = getRowSpan('emissionClassify', record, pftjData.value);
if (
rowIndex != 0 &&
pftjData.value[rowIndex - 1].emissionClassify == record.emissionClassify
) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '运行边界',
dataIndex: 'operatingBoundary',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
const rowSpan = getRowSpan('operatingBoundary', record, pftjData.value);
if (
rowIndex != 0 &&
pftjData.value[rowIndex - 1].operatingBoundary == record.operatingBoundary
) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '排放类别',
dataIndex: 'emissionCategory',
},
{
title: '排放源',
dataIndex: 'emissionSources',
},
{
title: '温室气体',
dataIndex: 'greenhouseGases',
},
];
tableHeader.value = Object.keys(data).map((key) => ({
title: data[key],
dataIndex: key,
}));
pftjColumn.value = pftjColumn.value.concat(tableHeader.value);
});
};
// 排放统计表头
const pftjColumn = ref([]);
// 合并单元格
const getRowSpan = (dataIndex: string, record: any, data: any, dependents: string[] = []) => {
let rowSpan = 1;
for (let i = data.indexOf(record) + 1; i < data.length; i++) {
let shouldMerge = true;
for (const dependent of dependents) {
if (data[i][dependent] !== record[dependent]) {
shouldMerge = false;
break;
}
}
if (shouldMerge && data[i][dataIndex] === record[dataIndex]) {
rowSpan++;
} else {
break;
}
}
return rowSpan;
};
// 碳排流向
const tplxChart = ref(null);
let chartInstance: echarts.ECharts | null = null;
const drawEcharts = () => {
chartInstance = echarts.init(tplxChart.value);
let listData = datalist.value;
let linkDatas = linksData.value;
let Color = [
'#61FEFF',
'#937FE6',
'#2B56D3',
'#87E7AA',
'#937FE6',
'#FF9B97',
'#8f23f5',
'#0576ea',
'#2cb8cf',
'#8A7EE0',
'#2cb8cf',
'#4e70f0',
'#1fa3de',
'#bbc951',
'#FFC14B',
'#b785a6',
'#c23531',
'#2f4554',
];
let Color1 = [
'#04E0F3',
'#682EFC',
'#35A7FE',
'#0DC09F',
'#682EFC',
'#ED6663',
'#8f23f5',
'#0576ea',
'#2cb8cf',
'#8A7EE0',
'#2cb8cf',
'#4e70f0',
'#1fa3de',
'#bbc951',
'#FFC14B',
'#b785a6',
'#c23531',
'#2f4554',
];
let sourceLabel = [
'right',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
'left',
];
let itemStyleColor = [];
let labelSource = [];
for (let i = 0; i < listData.length; i++) {
listData[i].label = {
normal: {
position: sourceLabel[i],
},
};
labelSource.push(sourceLabel[i]);
}
for (let d = 0; d < listData.length; d++) {
listData[d].itemStyle = {
normal: {
color: {
type: 'linear',
x: 0,
y: 1,
x2: 0,
y2: 0,
colorStops: [
{
offset: 1,
color: Color[d], // 0% 处的颜色
},
{
offset: 0,
color: Color1[d], // 100% 处的颜色
},
],
global: false, // 缺省为 false
},
},
};
itemStyleColor.push(listData[d]);
}
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'item',
triggerOn: 'mousemove',
},
series: [
{
type: 'sankey',
layout: 'none',
top: '4%',
bottom: '12%',
left: '20',
right: '20',
nodeGap: 15,
nodeWidth: 25,
focusNodeAdjacency: 'allEdges',
data: itemStyleColor,
links: linkDatas,
label: {
normal: {
color: '#fff',
fontSize: 14,
formatter: function (params) {
if (params.data.name == '公共平台') {
let strs = params.data.name.split(''); //字符串数组
let str = '';
for (let i = 0, s; (s = strs[i++]); ) {
//遍历字符串数组
str += s;
if (!(i % 1)) str += '\n'; //按需要求余
}
return '{white|' + str + '}';
} else {
return params.data.name;
}
},
rich: {
white: {
fontSize: 16,
lineHeight: 30,
padding: [0, 0, 0, -26],
},
},
},
},
lineStyle: {
normal: {
opacity: 0.4,
color: 'source',
curveness: 0.5,
},
},
itemStyle: {
normal: {
borderWidth: 1,
borderColor: 'transparent',
},
},
},
],
};
chartInstance = echarts.init(tplxChart.value);
chartInstance.setOption(option);
};
const handleTabChange = (key) => {
console.log('Tab changed:', key);
// 在这里可以执行需要在页面切换时执行的逻辑
if (key === '2') {
nextTick(() => {
getEmissionStatistic();
});
} else if (key === '3') {
nextTick(() => {
getCarbonFlowDirection();
});
}
};
// 点击返回
const emit = defineEmits(['change-data']);
const changeParentData = () => {
emit('change-data', true, false);
};
</script>
<style lang="less" scoped>
.main {
background-color: @ns-content-bg;
display: flex;
height: 100%;
}
.left {
width: 300px;
margin-right: @ns-gap;
min-width: fit-content;
> div {
background-color: @white;
}
display: flex;
flex-direction: column;
.top {
position: relative;
height: 100%;
margin-bottom: 20px;
.ns-form-title {
font-weight: bold;
user-select: text;
padding: 16px;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 1px solid #e9e9e9;
}
.title {
text-align: left;
height: 32px;
line-height: 32px;
font-weight: bold;
user-select: text;
position: relative;
padding-left: 9px;
}
.title::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 13px;
width: 3px;
border-radius: 1px;
background-color: #2778ff;
}
}
.bottom {
position: relative;
height: 20%;
.ns-form-title {
font-weight: bold;
user-select: text;
padding: 16px;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 1px solid #e9e9e9;
}
.title {
text-align: left;
height: 32px;
line-height: 32px;
font-weight: bold;
user-select: text;
position: relative;
padding-left: 9px;
}
.title::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 13px;
width: 3px;
border-radius: 1px;
background-color: #2778ff;
}
.button > div {
height: 3vh;
display: flex;
align-items: center;
}
.pftj {
background: #c9e4ff;
}
.tplx {
background: #c9e4ff;
}
}
}
.right {
flex: 1;
min-width: 0;
height: 100%;
background-color: @white;
border-radius: @ns-border-radius;
box-shadow: @ns-content-box-shadow;
position: relative;
:deep(.ant-tabs-content-holder) {
padding: 20px;
}
.ns-table-title {
text-align: left;
font-size: 16px;
font-weight: bold;
user-select: text;
width: 92%;
margin-bottom: @ns-gap;
}
.mainLeft {
width: 19%;
margin-right: 1%;
border-right: 1px solid #f2f2f2;
position: relative;
height: 96%;
.treeRow {
display: flex;
justify-content: space-between;
align-items: center;
}
.actionMore {
display: none;
}
:deep(.ant-tree-node-content-wrapper) {
&:hover {
.actionMore {
display: block;
}
}
}
.addTreeNode {
width: 100%;
padding: 16px;
position: absolute;
bottom: 0;
}
}
.mainRight {
width: 80%;
}
}
.search {
width: 70%;
height: 5vh;
display: flex;
align-items: center;
margin-left: 10%;
}
.treePart {
width: 70%;
height: 100%;
display: flex;
border: 1px solid #bfbfbf;
margin-left: 10%;
flex-direction: column;
}
:deep(.ant-upload.ant-upload-drag) {
height: 18vh;
margin-top: 10px;
}
</style>