Files
SaaS-lib/hx-ai-intelligent/src/view/carbonEmissionManage/carbonAssets/carbonAssetsDetail/index.vue
2024-08-26 16:12:26 +08:00

842 lines
26 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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="search">
<a-card style="border-radius: 12px">
<div class="ns-form-title">
<div class="title">查询</div>
<div class="operation">
<a-button type="primary" @click="changeParentData"> 返回 </a-button>
</div>
</div>
<a-form layout="inline">
<a-form-item>
<a-select
v-model:value="queryParams.accountType"
style="width: 200px"
placeholder="请输入账户类型">
<a-select-option :value="1">全国账户</a-select-option>
<a-select-option :value="2">地方账户</a-select-option>
<a-select-option :value="3">CCER</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-cascader
v-model:value="queryParams.transactionType"
multiple
style="width: 200px"
:options="options"
placeholder="请选择交易类型"
suffix-icon="Shopping Around">
<template #tagRender="data">
<a-tag :key="data.value" color="blue">{{ data.label }}</a-tag>
</template>
</a-cascader>
</a-form-item>
<a-form-item>
<a-date-picker
style="width: 200px"
v-model:value="queryParams.year"
placeholder="请选择账期"
picker="year"
valueFormat="YYYY" />
</a-form-item>
<a-form-item>
<a-button type="primary" @click="searchTableList">查询</a-button>
<a-button html-type="submit" style="margin-left: 6px" @click="reset">重置</a-button>
</a-form-item>
</a-form>
</a-card>
</div>
<div style="display: flex; margin-top: 20px; height: calc(84% - 20px)">
<div class="detailTable">
<ns-view-list-table v-bind="tableConfig" :model="data" ref="mainRef" :scroll="{ x: 2000 }">
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'accountType'">
<span v-if="record.accountType">{{ record.accountType.label }}</span>
</template>
</template>
</ns-view-list-table>
</div>
<div class="total">
<div class="ns-form-title">
<div class="title">配额统计</div>
<div class="operation" style="display: flex; justify-content: flex-end; width: 63%">
<a-button type="primary" v-if="queryParams.accountType === 1" @click="getTotalTable(1)">
全国配额
</a-button>
<a-button
type="primary"
v-if="queryParams.accountType === 2"
@click="getTotalTable(2)">
地方配额
</a-button>
<a-button type="primary" v-if="queryParams.accountType === 3" @click="getTotalTable(3)">
CCER配额
</a-button>
</div>
</div>
<a-table :columns="totalColumns" :data-source="totalData" bordered :pagination="false">
<template #bodyCell="{ column, text }">
<span>{{ text || '-' }}</span>
<!-- <template v-if="column.dataIndex === 'name'">
<a>{{ text }}</a>
</template> -->
</template>
</a-table>
</div>
</div>
<!-- 新增报告弹窗 -->
<a-drawer
:width="500"
:visible="visible"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
@close="onClose">
<div class="ns-form-title" style="display: flex">
<div class="title">{{ text }}</div>
</div>
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol">
<a-form-item ref="name" label="账户类型" name="accountType">
<a-select v-model:value="formState.accountType" placeholder="请输入账户类型">
<a-select-option value="1">全国账户</a-select-option>
<a-select-option value="2">地方账户</a-select-option>
<a-select-option value="3">CCER</a-select-option>
</a-select>
</a-form-item>
<a-form-item ref="name" label="交易类型" name="transactionType">
<a-cascader
v-model:value="formState.transactionType"
:options="options"
placeholder="请选择交易类型" />
</a-form-item>
<a-form-item ref="name" label="交易日期" name="transactionDate">
<a-date-picker v-model:value="formState.transactionDate" valueFormat="YYYY-MM-DD" />
</a-form-item>
<a-form-item ref="name" label="交易数量" name="transactionQuantity">
<a-input-number
v-model:value="formState.transactionQuantity"
:maxlength="11"
@keydown="handleKeyDown"
placeholder="请输入交易数量" />
</a-form-item>
<a-form-item ref="name" label="发生金额" name="amountIncurred">
<a-input-number
v-model:value="formState.amountIncurred"
:maxlength="16"
@keydown="handleKeyDown"
placeholder="请输入发生金额" />
</a-form-item>
<a-form-item ref="name" label="交易对象" name="tradingPartner">
<a-input
v-model:value="formState.tradingPartner"
:maxlength="20"
@keydown="handleKeyDown"
placeholder="请输入交易对象" />
</a-form-item>
<a-form-item ref="name" label="交易凭证">
<a-upload
:file-list="fileList"
name="file"
accept=".jpg,.jpeg,.png,.gif,.bmp,.pdf"
@remove="handleFileRemove"
:before-upload="beforeUpload"
@change="handleChange">
<a-button>
<upload-outlined></upload-outlined>
上传
</a-button>
</a-upload>
</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>
</template>
<script lang="ts" setup>
import { ref, defineEmits, toRaw } from 'vue';
import { http } from '/nerv-lib/util/http';
import { UploadOutlined } from '@ant-design/icons-vue';
import type { UploadChangeParam, UploadProps } from 'ant-design-vue';
import { Pagination, Modal, message } from 'ant-design-vue';
import {
carbonAssets,
carbonEmissionFactorLibrary,
uploadPic,
} from '/@/api/carbonEmissionFactorLibrary';
import { log } from 'console';
defineOptions({
energyType: 'carbonAssets', // 与页面路由name一致缓存才可生效
components: {
'a-pagination': Pagination,
},
});
// 父组件id
const props = defineProps({
parentId: {
type: Number,
},
});
const orgId = ref('');
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
orgId.value = result;
const fetch = (api, params = { orgId }, config) => {
return http.post(api, params, config);
};
// 详情部分变量
const selectedRowKeys = ref([]);
const onSelectionChange = (selectedKeys, selectedRows) => {
selectedRowKeys.value = selectedKeys;
};
const total = ref<number>();
const year = ref(new Date().getFullYear().toString());
const queryParams = ref({
pageNum: 1,
pageSize: 10,
orgId: orgId.value,
accountType: props.parentId,
year: year.value,
});
const searchTableList = () => {
year.value = queryParams.value.year;
transactionType.value = queryParams.value.transactionType;
accountType.value = queryParams.value.accountType;
getTotalTable(queryParams.value.accountType);
mainRef.value?.nsTableRef.reload();
// getDetailList();
};
// 获取左侧列表数据
const getDetailList = () => {
fetch(carbonAssets.carbonDetailsList, queryParams.value).then((res) => {
data.value = res.data.records;
total.value = res.data.total;
});
};
// getDetailList();
const columns = [
{
title: '序号',
width: 80,
customRender: (text: any) => {
return text.index + 1;
},
},
{
title: '资产类别',
dataIndex: 'accountType',
},
{
title: '交易方式',
dataIndex: 'transactionTypeName',
},
{
title: '交易日期',
dataIndex: 'transactionDate',
sorter: (a, b) => a.transactionDate - b.transactionDate,
},
{
title: '本期收入(tCO2)',
dataIndex: 'income',
sorter: (a, b) => a.income - b.income,
},
{
title: '本期支出(tCO2)',
dataIndex: 'expenditure',
sorter: (a, b) => a.expenditure - b.expenditure,
},
{
title: '发生金额(¥)',
dataIndex: 'amountIncurredValue',
},
{
title: '交易对象',
dataIndex: 'tradingPartner',
},
{
title: '更新人',
dataIndex: 'updateUser',
},
{
title: '更新时间',
dataIndex: 'updateTime',
},
{
title: '操作',
key: 'action',
width: 130,
},
];
const data = ref([]);
const reset = () => {
queryParams.value = {
pageNum: 1,
pageSize: 10,
orgId: orgId.value,
accountType: props.parentId,
year: new Date().getFullYear().toString(),
};
accountType.value = props.parentId;
year.value = new Date().getFullYear();
transactionType.value = '';
mainRef.value?.nsTableRef.reload();
// getDetailList();
};
const editData = (record) => {
getDictList();
text.value = '编辑';
visible.value = true;
formState.value.id = record.id;
fetch(uploadPic.select, { bizId: record.id, bizType: 1 }).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格式
}));
});
formState.value = JSON.parse(JSON.stringify(record));
// if (formState.value.expenditure === 0) {
// formState.value.transactionQuantity = formState.value.income;
// } else {
// formState.value.transactionQuantity = formState.value.expenditure;
// }
setTimeout(() => {
let selectDevice = ref([Number(formState.value.transactionType)]);
findParentIds(options.value, formState.value.transactionType, selectDevice.value);
formState.value.transactionType = selectDevice;
formState.value.transactionType = formState.value.transactionType;
}, 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 delData = (record) => {
const id = [record.id];
Modal.confirm({
title: '警告',
content: '确定要删除吗?',
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
fetch(carbonAssets.delete, { ids: id }).then((res) => {
message.success('操作成功!');
getDetailList();
});
},
onCancel() {
console.log('Cancel');
},
});
};
const deleteMore = () => {
Modal.confirm({
title: '警告',
content: '确定要删除吗?',
okText: '确定',
okType: 'primary',
cancelText: '取消',
onOk() {
fetch(carbonAssets.delete, { ids: selectedRowKeys.value }).then((res) => {
message.success('操作成功!');
getDetailList();
});
},
onCancel() {
console.log('Cancel');
},
});
};
const mainRef = ref();
const transactionType = ref();
const accountType = ref();
accountType.value = props.parentId;
const tableConfig = ref({
title: '数据库',
api: carbonAssets.carbonDetailsList,
params: {
orgId,
accountType,
year,
},
headerActions: [
{
label: '新增',
name: 'userAdd',
type: 'primary',
handle: () => {
text.value = '新增';
visible.value = true;
getDictList();
},
},
{
label: '导入',
type: 'primary',
name: 'carbonAssetsImport',
extra: {
api: carbonAssets.import, // 导入接口名
params: {
orgId,
year,
},
title: '碳资产', // 弹窗title
templateName: 'carbonAssets', // 所使用的文件名称
indexName: '碳资产', // 匹配类型字段
message: [
{ label: '1、若必填项未填写则不能进行导入操作' },
{ label: `2、当重复时则更新数据。` },
{ label: '3、数据将从模版的第五行进行导入。' },
{ label: '4、文件导入勿超过5MB。' },
],
},
},
{
label: '导出',
type: 'primary',
handle: () => {
const exportQuery = ref({
orgId: orgId.value,
pageNum: 1,
pageSize: 999,
year: queryParams.value.year,
ids: selectedRowKeys.value,
accountType: props.parentId,
});
const config = {
responseType: 'blob',
};
fetch(carbonAssets.export, exportQuery.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', 'carbonTradeDetails.xlsx'); // 设置下载的文件名
document.body.appendChild(link);
link.click();
// 清理 URL 对象
window.URL.revokeObjectURL(url);
selectedRowKeys.value = [];
})
.catch((error) => {
console.error('下载失败:', error);
});
},
},
{
label: '批量删除',
type: 'primary',
name: 'userBatchDel',
dynamicDisabled: (data: any) => {
return data.list.length === 0;
},
confirm: true,
isReload: true,
isClearCheck: true,
api: carbonAssets.delete,
dynamicParams: { ids: 'id[]' },
},
],
columns: [
{
title: '序号',
width: 80,
customRender: (text: any) => {
return text.index + 1;
},
},
{
title: '资产类别',
dataIndex: 'accountType',
},
{
title: '交易方式',
dataIndex: 'transactionTypeName',
},
{
title: '交易日期',
dataIndex: 'transactionDate',
sorter: (a, b) => a.transactionDate - b.transactionDate,
},
{
title: '本期收入(tCO2)',
dataIndex: 'income',
sorter: (a, b) => a.income - b.income,
},
{
title: '本期支出(tCO2)',
dataIndex: 'expenditure',
sorter: (a, b) => a.expenditure - b.expenditure,
},
{
title: '发生金额(¥)',
dataIndex: 'amountIncurredValue',
},
{
title: '交易对象',
dataIndex: 'tradingPartner',
},
{
title: '更新人',
dataIndex: 'updateUser',
},
{
title: '更新时间',
dataIndex: 'updateTime',
},
],
columnActions: {
title: '操作',
actions: [
{
label: '编辑',
name: 'userEdit',
handle: (record: any) => {
getDictList();
text.value = '编辑';
visible.value = true;
formState.value.id = record.id;
fetch(uploadPic.select, { bizId: record.id, bizType: 1 }).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格式
}));
});
formState.value = JSON.parse(JSON.stringify(record));
setTimeout(() => {
let selectDevice = ref([Number(formState.value.transactionType)]);
findParentIds(options.value, formState.value.transactionType, selectDevice.value);
formState.value.transactionType = selectDevice;
formState.value.transactionType = formState.value.transactionType;
}, 500);
},
},
{
label: '删除',
name: 'userDelete',
dynamicParams: { ids: 'id[]' },
confirm: true,
isReload: true,
api: carbonAssets.delete,
},
],
},
formConfig: {
schemas: [],
params: {},
},
rowKey: 'id',
});
// 分页器
const onChange = (pageNumber: number, size: number) => {
queryParams.value.pageNum = pageNumber;
queryParams.value.pageSize = size;
getDetailList();
};
// 新增相关数据
const visible = ref(false);
const text = ref('新增');
const formState = ref({
orgId: orgId.value,
});
const formRef = ref();
const labelCol = { span: 5 };
const wrapperCol = { span: 19 };
const options = ref([]);
// 获取字典值
const getDictList = () => {
fetch(carbonEmissionFactorLibrary.dictionaryUnitManagement, { grp: 'TRANSACTION_TYPE' }).then(
(res) => {
options.value = res.data;
options.value = options.value.map((item) => ({
value: item.id,
label: item.cnValue,
children: item.children
? item.children.map((child) => ({
value: child.id,
label: child.cnValue,
}))
: [],
}));
},
);
};
getDictList();
// 点击新增
const addDetail = () => {
text.value = '新增';
visible.value = true;
getDictList();
};
const importFileList = ref<UploadProps['fileList']>([]);
const importFile = (options: UploadRequestOption) => {
const { file, onSuccess, onError } = options;
const formData = ref(new FormData());
formData.value.append('file', file as any);
formData.value.append('orgId', orgId.value);
formData.value.append('year', queryParams.value.year);
fetch(carbonAssets.import, formData.value)
.then((res) => {
message.success('操作成功!');
getDetailList();
})
.catch((error) => {
console.log('error', error);
});
};
const exportFile = () => {
const exportQuery = ref({
orgId: orgId.value,
pageNum: 1,
pageSize: 999,
year: queryParams.value.year,
ids: selectedRowKeys.value,
});
const config = {
responseType: 'blob',
};
fetch(carbonAssets.export, exportQuery.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', 'carbonTradeDetails.xlsx'); // 设置下载的文件名
document.body.appendChild(link);
link.click();
// 清理 URL 对象
window.URL.revokeObjectURL(url);
selectedRowKeys.value = [];
})
.catch((error) => {
console.error('下载失败:', error);
});
};
// 上传附件
const fileList = ref<UploadProps['fileList']>([]);
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
return false;
};
const handleChange = (info: UploadChangeParam) => {
fileList.value = [...info.fileList];
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
message.success(`${info.file.name} 文件上传成功`);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} 文件上传失败`);
}
};
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 handleKeyDown = (event: KeyboardEvent) => {
// Check if the pressed key is a space
if (event.code === 'Space') {
event.preventDefault();
}
};
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
console.log('values', formState, toRaw(formState));
formState.value.orgId = orgId.value;
if (formState.value.transactionType) {
formState.value.transactionType = formState.value.transactionType.join(',').split(',')[1];
}
if (formState.value.accountType.value) {
formState.value.accountType = formState.value.accountType.value;
}
fetch(carbonAssets.createOrUpdate, formState.value).then((res) => {
if (res.data.id && fileList.value.length !== 0) {
// uploadQuery.value.bizId = res.data.id;
const formData = ref(new FormData());
fileList.value.forEach((file) => {
if (file.type !== 'done') {
formData.value.append('files', file.originFileObj);
}
});
formData.value.append('bizType', 1);
formData.value.append('bizId', res.data.id);
delIds.value.forEach((item) => {
formData.value.append('deleteList', item);
});
fetch(uploadPic.uploadfiles, formData.value)
.then((res) => {
message.success('操作成功!');
visible.value = false;
delIds.value = [];
// getDetailList();
mainRef.value?.nsTableRef.reload();
getTotalTable();
})
.catch((error) => {
console.log('error', error);
});
} else {
message.success('操作成功!');
visible.value = false;
delIds.value = [];
// getDetailList();
mainRef.value?.nsTableRef.reload();
}
});
})
.catch((error) => {
console.log('error', error);
});
};
// 定义form表单的必填
const rules: Record<string, Rule[]> = {
accountType: [{ required: true, message: '请输入账户类型', trigger: 'change' }],
transactionType: [{ required: true, message: '请选择交易类型', trigger: 'change' }],
transactionDate: [{ required: true, message: '请选择交易日期', trigger: 'change' }],
transactionQuantity: [{ required: true, message: '请输入交易数量', trigger: 'change' }],
amountIncurred: [{ required: true, message: '请输入发生金额', trigger: 'change' }],
tradingPartner: [{ required: true, message: '请输入交易对象', trigger: 'change' }],
};
// 关闭新增抽屉
const onClose = () => {
visible.value = false;
delIds.value = [];
formState.value = {};
fileList.value = [];
formRef.value.resetFields();
};
// 统计表格
const getTotalTable = (type) => {
if (type) {
queryParams.value.accountType = type;
}
fetch(carbonAssets.quotaStatistics, queryParams.value).then((res) => {
totalData.value = res.data;
});
};
getTotalTable();
const totalColumns = [
{
title: '统计类型',
dataIndex: 'statisticType',
},
{
title: '小计',
dataIndex: 'subtotal',
},
{
title: '合计',
dataIndex: 'amountTo',
},
];
const totalData = ref([]);
// 点击返回
const emit = defineEmits(['change-data']);
const changeParentData = () => {
emit('change-data', true);
};
</script>
<style lang="less" scoped>
.ns-form-title {
font-weight: bold;
user-select: text;
padding-bottom: 10px;
display: flex;
justify-content: space-between;
}
.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;
}
:deep(.ant-card-body) {
padding: 16px;
}
.search {
height: 16%;
}
.detailTable {
width: 70%;
margin-right: 20px;
height: 100%;
background: white;
border-radius: 12px;
padding: 16px;
}
.total {
width: calc(30% - 20px);
height: 100%;
background: white;
border-radius: 12px;
padding: 16px;
}
:deep(.ns-table-search) {
display: none;
}
:deep(.ns-table-header) {
padding-top: unset !important;
align-items: unset !important;
padding-bottom: 10px;
height: unset !important;
}
:deep(.ns-basic-table) {
padding-top: unset !important;
}
:deep(.ns-table-main) {
padding: unset !important;
margin: unset !important;
border-radius: unset !important;
}
</style>