Files
SaaS-lib/hx-ai-intelligent/src/view/carbonEmissionManage/carbonAssets/carbonAssetsDetail/index.vue
2024-08-30 15:27:54 +08:00

895 lines
27 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="transactionType"
multiple
style="width: 200px"
:options="options"
@change="changeSelect"
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="请选择账期"
:allowClear="false"
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(85% - 20px)">
<div class="detailTable">
<ns-view-list-table
class="content-table"
v-bind="tableConfig"
:model="data"
ref="mainRef"
:scroll="{ x: 1280 }">
<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>
</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 transactionType = ref();
const queryParams = ref({
pageNum: 1,
pageSize: 10,
orgId: orgId.value,
accountType: props.parentId,
year: year.value,
});
const transactionTypeValue = ref();
const changeSelect = (value, selectedOptions) => {
transactionTypeValue.value = selectedOptions.flatMap((group) =>
group.flatMap((node) => [node.value, ...(node.children?.map((child) => child.value) || [])]),
);
// transactionTypeValue.value = value.flat()
};
const searchTableList = () => {
year.value = queryParams.value.year;
transactionTypeList.value = transactionTypeValue.value;
queryParams.value.transactionTypeList = transactionTypeValue.value;
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 = '';
transactionTypeList.value = [];
queryParams.value.transactionTypeList = transactionTypeList.value;
getTotalTable();
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 transactionTypeList = ref([]);
const accountType = ref();
accountType.value = props.parentId;
const tableConfig = ref({
title: '数据库',
api: carbonAssets.carbonDetailsList,
params: {
orgId,
accountType,
transactionTypeList,
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', '碳资产导出.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',
width: 100,
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
{
title: '交易方式',
dataIndex: 'transactionTypeName',
width: 100,
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
{
title: '交易日期',
dataIndex: 'transactionDate',
width: 100,
sorter: (a, b) => a.transactionDate - b.transactionDate,
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
{
title: '本期收入(tCO2)',
dataIndex: 'income',
width: 150,
sorter: (a, b) => a.income - b.income,
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
{
title: '本期支出(tCO2)',
dataIndex: 'expenditure',
width: 150,
sorter: (a, b) => a.expenditure - b.expenditure,
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
{
title: '发生金额(¥)',
width: 150,
dataIndex: 'amountIncurredValue',
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
{
title: '交易对象',
width: 100,
dataIndex: 'tradingPartner',
customRender: (text: any) => {
return text.text !== '' ? text : '-';
},
},
{
title: '更新人',
width: 100,
dataIndex: 'updateUser',
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
{
title: '更新时间',
width: 150,
ellipsis: true,
dataIndex: 'updateTime',
customRender: (text: any) => {
return text.text !== undefined ? text : '-';
},
},
],
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;
formState.value = {};
delIds.value = [];
// getDetailList();
mainRef.value?.nsTableRef.reload();
getTotalTable();
})
.catch((error) => {
console.log('error', error);
});
} else {
message.success('操作成功!');
visible.value = false;
delIds.value = [];
formState.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: 15%;
}
.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>