/* * @Author: code4eat awesomedema@gmail.com * @Date: 2023-03-03 11:30:33 * @LastEditors: code4eat awesomedema@gmail.com * @LastEditTime: 2024-12-05 15:11:30 * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ import KCIMPagecontainer from '@/components/KCIMPageContainer'; import { KCIMTable } from '@/components/KCIMTable'; import { createFromIconfontCN } from '@ant-design/icons'; import { ActionType, ProFormInstance } from '@ant-design/pro-components'; import { ProColumns } from '@ant-design/pro-table'; import { Modal, message, Tabs, Input, DatePicker, Popover, Alert } from 'antd'; import { Key, useEffect, useRef, useState } from 'react'; import * as XLSX from 'xlsx'; import { saveAs } from 'file-saver'; import moment from 'moment'; import 'moment/locale/zh-cn'; import locale from 'antd/es/date-picker/locale/zh_CN'; import { computeProfitReq, getNextLevelTableData, getReportDataReq, getReportProjectSettingList, getResponsibleCenters } from './service'; import './style.less'; import React from 'react'; import { getDicDataBySysId, getParamsDataBySysId } from '@/services/getDic'; import { KCIMLeftList } from '@/components/KCIMLeftList'; import { formatMoneyNumber } from '@/utils/format'; import { useModel } from '@umijs/max'; import ReportExport from '../reportExport/report'; import { getUserHasReports } from '../costAccounting/calcPageTemplate/service'; const { RangePicker } = DatePicker; const IconFont = createFromIconfontCN({ scriptUrl: '', }); let currentRow: any = undefined; function findAllParents(tree: any[]) { let parents: any[] = []; // 递归函数来遍历树并找到所有父节点 function traverse(nodes: any[]) { for (const node of nodes) { // 检查节点是否有子节点 if (node.children && node.children.length > 0) { parents.push(node); // 添加到父节点列表 traverse(node.children); // 递归遍历子节点 } } } traverse(tree); // 开始遍历树 return parents; // 返回所有父节点的数组 } function countLeafNodesRecursive(node: any) { // 如果当前节点没有子节点,说明它是一个叶子节点 if (!node.children || node.children.length === 0) { return 1; } let leafCount = 0; // 递归计算每个子节点的叶子节点数 for (let i = 0; i < node.children.length; i++) { leafCount += countLeafNodesRecursive(node.children[i]); } return leafCount; } // 数据转换函数 const transformData = (data: any[]) => { const resultMap: Map = new Map(); // 转换单个条目 const transformEntry = (entry: any, computeDate: string) => { if (!entry) return {}; const transformedEntry = { ...entry }; transformedEntry[`${computeDate}_amount`] = entry.amount != null ? formatMoneyNumber(entry.amount) : null; transformedEntry[`${computeDate}_percent`] = entry.percent != null ? `${((entry.percent * 100).toFixed(2))}%` : null; return transformedEntry; }; // 合并新的条目到现有条目,不覆盖已有的字段 const mergeEntries = (existingEntry: any, newEntry: any) => { for (const key in newEntry) { if (!existingEntry[key] || key.endsWith('_amount') || key.endsWith('_percent')) { existingEntry[key] = newEntry[key]; } } }; // 递归处理每个节点及其子节点 const processNode = (node: any, computeDate: string): any => { if (!node) return {}; const transformedNode = transformEntry(node, computeDate); if (node.children && Array.isArray(node.children)) { const processedChildren = node.children.map((child: any) => processNode(child, computeDate)); transformedNode.children = mergeChildren(transformedNode.children || [], processedChildren); } return transformedNode; }; // 合并子节点,确保同一个父节点下的子节点是唯一的 const mergeChildren = (existingChildren: any[], newChildren: any[]) => { newChildren.forEach((newChild) => { const existingChild = existingChildren.find((child) => child.reportName === newChild.reportName && child.key === newChild.key); if (existingChild) { mergeEntries(existingChild, newChild); if (newChild.children && Array.isArray(newChild.children)) { existingChild.children = mergeChildren(existingChild.children || [], newChild.children); } } else { existingChildren.push(newChild); } }); return existingChildren; }; // 主处理逻辑 data.forEach((item) => { const { computeDate, profitVoList } = item; if (!profitVoList || !Array.isArray(profitVoList)) { return; } profitVoList.forEach((profit) => { if (!resultMap.has(profit.reportName)) { resultMap.set(profit.reportName, { ...transformEntry(profit, computeDate), key: profit.reportName, }); } const existingEntry = resultMap.get(profit.reportName)!; const transformedProfit = processNode(profit, computeDate); mergeEntries(existingEntry, transformedProfit); if (transformedProfit.children && Array.isArray(transformedProfit.children)) { existingEntry.children = mergeChildren(existingEntry.children || [], transformedProfit.children); } }); }); // 确保每个层级的数据都有完整的月份字段 const fillMissingMonths = (nodes: any[], computeDates: string[]) => { nodes.forEach((node) => { computeDates.forEach((date) => { if (!node.hasOwnProperty(`${date}_amount`)) { node[`${date}_amount`] = node.amount != null ? formatMoneyNumber(node.amount) : null; } if (!node.hasOwnProperty(`${date}_percent`)) { node[`${date}_percent`] = node.percent != null ? `${((node.percent * 100).toFixed(2))}%` : null; } }); if (node.children && Array.isArray(node.children)) { fillMissingMonths(node.children, computeDates); if (node.children.length === 0) { delete node.children; } } }); }; const computeDates = data.map((item) => item.computeDate); const transformedData = Array.from(resultMap.values()); fillMissingMonths(transformedData, computeDates); transformedData.forEach((node) => { if (node.children && node.children.length === 0) { delete node.children; } }); // console.log({ transformedData }); return transformedData; }; const getNextUnexpandedKeys = (data: any[], expandedKeys: any[] = []) => { let keys: any[] = []; const traverse = (nodes: any) => { for (const node of nodes) { // 如果当前节点还没有展开,就把它加入 keys if (!expandedKeys.includes(node.id)) { keys.push(node.id); } // 如果当前节点已经展开,继续遍历子节点 if (node.children && expandedKeys.includes(node.id)) { traverse(node.children); } } }; traverse(data); return keys; }; export default function DepartmentCostCalc() { const [tableDataFilterParams, set_tableDataFilterParams] = useState({ reportType: 0 }); const tableRef = useRef(); const [tabs, set_tabs] = useState([]); const { initialState, setInitialState } = useModel('@@initialState'); const [computeRangeDate, set_computeRangeDate] = useState(initialState ? [initialState.computeDate, initialState.computeDate] : []); const [responsibleCenters, set_responsibleCenters] = useState([]); const [currentTabKey, set_currentTabKey] = useState(undefined); const [currentTab, set_currentTab] = useState(undefined); const [currentSelectedRespon, set_currentSelectedRespon] = useState(undefined); const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState(''); const [allParentsKeys, set_allParentsKeys] = useState([]); const [dataSource, set_dataSource] = useState([]); const [calcResultText, set_calcResultText] = useState(undefined); const [nextLevel, set_nextLevel] = useState(undefined); const [columns, set_columns] = useState([]); const [needShowReportCode, set_needShowReportCode] = useState('0'); // 动态生成列定义函数 const generateDataColumns = async (data: any[]) => { const baseColumns = [ { title: '报表项目名称', dataIndex: 'reportName', key: 'reportName', width: 200, fixed: 'left' }, ]; let ifShowPercent = true; const { systemId } = JSON.parse(localStorage.getItem('currentSelectedTab') as string) const resp = await getParamsDataBySysId(systemId, '1851077044079824896'); if (resp) { ifShowPercent = resp.value == '1' ? true : false; } // 获取所有月份并生成列 const monthColumns = data.map((item) => { const month = item.computeDate; const base = [ { title: '金额', dataIndex: `${month}_amount`, key: `${month}_amount`, width: 100, align: 'right', renderText(num: number, record: any) { const { calcType } = record; if (calcType == 0) { return } else { if (calcType == 1 || calcType == 2 || calcType == 5) { return { set_nextLevel({ ...record, date: month }); tableRef.current?.reload() }} style={{ color: (calcType == 1 || calcType == 2 || calcType == 5) ? '#3377FF' : '#17181A', cursor: (calcType == 1 || calcType == 2 || calcType == 5) ? 'pointer' : 'default' }}>{num}; } else { return num } //return formatMoneyNumber(num); } }, }, ] return { title: month, children: ifShowPercent ? [ ...base, { title: '占比', dataIndex: `${month}_percent`, key: `${month}_percent`, width: 100, align: 'right', renderText(text: number, record: any) { const { calcType } = record; if (calcType == 0) { return } else { return text } }, }, ] : [...base] }; }); set_columns([...baseColumns, ...monthColumns]); }; const getIfShowReport = async () => { const { systemId } = JSON.parse(localStorage.getItem('currentSelectedTab') as string) const resp = await getParamsDataBySysId(systemId, '1845698984623083520'); if (resp) { set_needShowReportCode(resp.value) } } const getTableData = async (params: any) => { const { responsibilityCode, filter = undefined } = params; if (!responsibilityCode) return [] if (!nextLevel) { const resp = await getReportProjectSettingList({ ...params }); if (resp) { // if (filter) { // const filterData = searchTree(resp, filter); // const allParents = findAllParents(filterData); // set_allParentsKeys([...(allParents.map((a: any) => a.id))]) // return { // data: filterData, // success: true, // } // } const realData = transformData(resp); if (currentTab.value == '1') { const allParents = findAllParents(realData); set_allParentsKeys([...(allParents.map((a: any) => a.id))]); } generateDataColumns(resp) set_dataSource(realData); return { data: realData, success: true, } } } if (nextLevel) { const { reportId, calcType, date } = nextLevel; if (calcType == 1) { set_columns([ { title: '会计科目代码', dataIndex: 'accountCode', key: 'accountCode', }, { title: '会计项目名称', dataIndex: 'accountName', key: 'accountName', }, { title: '金额', dataIndex: 'amount', key: 'amount', align: 'right', renderText(num: number, record: any) { return formatMoneyNumber(num) } } ]) const resp = await getNextLevelTableData({ computeDate: date, reportId, reportType: currentTabKey, responsibilityCode: currentSelectedRespon.responsibilityCode }); return { data: resp ? resp : [], success: true, } } if (calcType == 2 || calcType == 5) { const columns = [ { title: () => , key: 'empty', children: [ { title: '责任中心', dataIndex: 'responsibilityCenter', key: 'responsibilityCenter', render: (text: any, row: { responsibilityCenterRowSpan: any; }) => { return { children: text, props: { rowSpan: row.responsibilityCenterRowSpan, }, }; }, }, { title: '项目名称', dataIndex: 'projectName', key: 'projectName', render: (text: any, row: { projectNameRowSpan: any; }) => { return { children: text, props: { rowSpan: row.projectNameRowSpan, }, }; }, }, { title: '分摊参数', dataIndex: 'shareParam', key: 'shareParam', }, { title: '分摊金额', dataIndex: 'shareAmount', key: 'shareAmount', align: 'right' }, ] }, { title: `${currentSelectedRespon.responsibilityName}`, dataIndex: `${currentSelectedRespon.responsibilityCode}`, key: `${currentSelectedRespon.responsibilityCode}`, children: [ { title: '分摊参数数值', dataIndex: 'shareParamValue', key: 'shareParamValue', }, { title: '分摊参数占比', dataIndex: 'shareParamRate', key: 'shareParamRate', }, { title: '金额', dataIndex: 'amount', key: 'amount', }, ] } ]; set_columns([...columns]); const resp = await getNextLevelTableData({ computeDate: date, reportId, reportType: currentTabKey, responsibilityCode: currentSelectedRespon.responsibilityCode }); const processData = (data: any[]) => { const rows: any[] = []; data.forEach((item, index) => { let responseNameRowSpan = 0; // 合并相同责任中心的单元格 item.dataList.forEach((dataItem: any) => { responseNameRowSpan += dataItem.shareParamData.length; // 计算需要合并的行数 }); let currentRow = 0; // 当前行位置 item.dataList.forEach((dataItem: any) => { const dataItemRowSpan = dataItem.shareParamData.length; // 项目名称的行合并数 dataItem.shareParamData.forEach((param: any, paramIndex: number) => { rows.push({ key: `${item.responseCode}-${dataItem.alias}-${param.shareParamCode}`, responsibilityCenter: paramIndex === 0 && currentRow === 0 ? item.responseName : '', responsibilityCenterRowSpan: paramIndex === 0 && currentRow === 0 ? responseNameRowSpan : 0, projectName: paramIndex === 0 ? dataItem.alias : '', projectNameRowSpan: paramIndex === 0 ? dataItemRowSpan : 0, shareParam: param.shareParamName, shareAmount: formatMoneyNumber(param.shareParamAmount), shareParamValue: param.shareParamNum, shareParamRate: param.shareParamRate, amount: formatMoneyNumber(param.shareParamValue), }); }); currentRow++; }); }); return rows; }; return { data: resp ? processData(resp) : [], success: true, } } } return [] } const onTabChanged = (key: Key) => { set_currentTabKey(key); const needItem = tabs.filter((a) => a.key == key); if (needItem.length > 0) set_currentTab(needItem[0]) } const getTabs = async () => { // const { systemId } = JSON.parse((localStorage.getItem('currentSelectedTab')) as string) // const resp = await getDicDataBySysId(systemId, 'PROFIT_REPORT_TYPE'); const resp = await getUserHasReports(); if (resp) { const { dataVoList } = resp; const tempArr = dataVoList.map((a: any) => ({ label: a.name, key: Number(a.code), value: a.value })); const arr = (tempArr.filter((a: any) => a.value != '2')); set_tabs([...arr]); set_currentTabKey(arr[0].key); set_currentTab(arr[0]); } } const getResponsibleCenterList = async (reportType: string) => { const resp = await getResponsibleCenters(reportType); if (resp) { set_responsibleCenters(resp); } } const onLeftChange = (currentSelected: any) => { set_currentSelectedRespon(currentSelected); } const backToHandle = () => { set_nextLevel(undefined); tableRef.current?.reload(); } const tableDataSearchHandle = (paramName: string) => { set_tableDataFilterParams({ ...tableDataFilterParams, [`${paramName}`]: tableDataSearchKeywords }) } const getHeaderRows = (columns: any[], level = 0, headerRows: any[] = [], maxLevel = 0) => { headerRows[level] = headerRows[level] || []; columns.forEach((col: { title: any; children: any; }) => { const colSpan = getColSpan(col); headerRows[level].push({ title: col.title, colSpan, rowSpan: col.children ? 1 : maxLevel - level }); if (col.children) { getHeaderRows(col.children, level + 1, headerRows, maxLevel); } else { // 填充空白单元格 for (let i = level + 1; i < maxLevel; i++) { headerRows[i] = headerRows[i] || []; headerRows[i].push({ title: '', colSpan: 1, rowSpan: 1 }); } } }); return headerRows; }; const getColSpan: any = (col: { children: any[]; }) => { if (!col.children) return 1; return col.children.reduce((sum, child) => sum + getColSpan(child), 0); }; const getMaxLevel = (col: any) => { if (!col.children) return 1; return 1 + Math.max(...col.children.map(getMaxLevel)); }; const extractLeafColumns = (columns: any[]) => { let leafColumns: any[] = []; columns.forEach(col => { if (col.children) { leafColumns = leafColumns.concat(extractLeafColumns(col.children)); } else { leafColumns.push(col); } }); return leafColumns; }; const addRowWithIndentation = (record: any, level: number, leafColumns: any[], worksheetData: any[]) => { const row = leafColumns.map(col => record[col.dataIndex] ?? ''); row[0] = ' '.repeat(level * 4) + row[0]; // 在第一列前添加缩进空格以表示层级 worksheetData.push(row); if (record.children) { record.children.forEach((child: any) => addRowWithIndentation(child, level + 1, leafColumns, worksheetData)); } }; const handleExport = () => { try { const workbook = XLSX.utils.book_new(); const worksheetData: any[] = []; // 获取最大层级 const maxLevel = columns.reduce((max, col) => Math.max(max, getMaxLevel(col)), 0); // 生成多层级表头 const headerRows = getHeaderRows(columns, 0, [], maxLevel); // 构建表头行 headerRows.forEach((row: any, rowIndex) => { const rowData: string[] = []; row.forEach((cell: { title: any; colSpan: number; rowSpan: number; }) => { rowData.push(cell.title); for (let i = 1; i < cell.colSpan; i++) { rowData.push(''); } }); worksheetData.push(rowData); }); // 填充单层表头的空白行 if (maxLevel > 1) { const numColumns = headerRows[0].reduce((sum: any, cell: { colSpan: any; }) => sum + cell.colSpan, 0); for (let i = 1; i < maxLevel; i++) { while (worksheetData[i].length < numColumns) { worksheetData[i].push(''); } } } // 提取最内层表头列 const leafColumns = extractLeafColumns(columns); // 添加数据并处理树结构 dataSource.forEach(record => addRowWithIndentation(record, 0, leafColumns, worksheetData)); const worksheet = XLSX.utils.aoa_to_sheet(worksheetData); // 初始化合并单元格数组 worksheet['!merges'] = worksheet['!merges'] || []; // 合并单元格 headerRows.forEach((row: any, rowIndex) => { let colIndex = 0; row.forEach((cell: { colSpan: number; rowSpan: number; }) => { if (cell.colSpan > 1 || cell.rowSpan > 1) { worksheet['!merges']!.push({ // 使用非空断言 '!' s: { r: rowIndex, c: colIndex }, e: { r: rowIndex + cell.rowSpan - 1, c: colIndex + cell.colSpan - 1 } }); } colIndex += cell.colSpan; }); }); // 设置单元格对齐方式 Object.keys(worksheet).forEach(cell => { if (cell[0] !== '!') { worksheet[cell].s = { alignment: { vertical: 'center', horizontal: 'center' } }; } }); XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'binary' }); const s2ab = (s: string) => { const buf = new ArrayBuffer(s.length); const view = new Uint8Array(buf); for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xFF; return buf; }; saveAs(new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), currentTab ? `${currentTab.label}.xlsx` : 'table_data.xlsx'); } catch (error) { console.error('Export failed:', error); } }; const handleExpandNext = () => { // 当前所有未展开的节点,第一层优先展开 const keysToExpand = getNextUnexpandedKeys(dataSource, allParentsKeys); set_allParentsKeys((prev) => Array.from(new Set([...prev, ...keysToExpand]))); }; const handleCollapseAll = () => { set_allParentsKeys([]); }; useEffect(() => { if (computeRangeDate && currentTabKey != undefined) { getResponsibleCenterList(currentTabKey); set_nextLevel(undefined); set_allParentsKeys([]); } }, [computeRangeDate, currentTabKey]); useEffect(() => { if (currentSelectedRespon) { set_tableDataFilterParams({ ...tableDataFilterParams, responsibilityCode: currentSelectedRespon.responsibilityCode, reportType: currentTabKey, beginComputeDate: computeRangeDate[0], endComputeDate: computeRangeDate[1], }); set_nextLevel(undefined); } set_allParentsKeys([]); }, [currentSelectedRespon]) useEffect(() => { getTabs(); getIfShowReport(); }, []) return (
核算年月: { // console.log({data,dateString}); set_computeRangeDate(dateString); setInitialState((s: any) => ({ ...s, computeDate: dateString[0], })) set_tableDataFilterParams({ ...tableDataFilterParams, beginComputeDate: dateString[0], endComputeDate: dateString[1], }); }} picker="month" locale={locale} autoComplete="off" defaultValue={[moment(computeRangeDate[0], 'YYYY-MM'), moment(computeRangeDate[1], 'YYYY-MM')]} format="YYYY-MM" />
{/*
onekeyComputeProfitHandle()}>一键计算
*/}
0 ? tabs[0].key : undefined} items={tabs} key={'key'} onChange={(key) => onTabChanged(key)} /> {calcResultText && set_calcResultText(undefined)} icon={} closable style={{ padding: '4px 12px', marginBottom: 16, borderRadius: 4, border: (calcResultText.indexOf('失败') != -1) ? '1px solid #73E6BF' : '', background: (calcResultText.indexOf('失败') != -1) ? '#FFF1F3' : '#EBFFF8' }} message={calcResultText} type={calcResultText.indexOf('失败') != -1 ? 'error' : 'success'} />}
{/* 检索: tableDataSearchHandle('filter')} /> } onChange={(e) => { set_tableDataSearchKeywords(e.target.value); if (e.target.value.length == 0) { set_tableDataFilterParams({ ...tableDataFilterParams, filter: '' }); } }} onPressEnter={(e) => { set_tableDataFilterParams({ ...tableDataFilterParams, filter: ((e.target) as HTMLInputElement).value }); }} /> */} {nextLevel &&
backToHandle()}>{nextLevel.reportName}
}
{(!nextLevel||(nextLevel&&needShowReportCode == '0'))&&(
{/* openTableDataDrawer()}>报表数据 */} handleCollapseAll()}>全部折叠 handleExpandNext()}>展开下一层 {!nextLevel && handleExport()}>导出}
)}
{needShowReportCode != '0'&&nextLevel&& ( )} {(needShowReportCode == '0'||(needShowReportCode != '0'&&(!nextLevel))) && ( (record.children ? 'has-children hover-row' : 'hover-row')} expandable={{ defaultExpandAllRows: true, expandedRowKeys: allParentsKeys, onExpand(expanded, record) { const { id } = record; if (!expanded) { const expandedKeys = allParentsKeys.filter(a => a != id); set_allParentsKeys([...expandedKeys]); } else { set_allParentsKeys([...allParentsKeys, id]); } }, }} columns={columns as ProColumns[]} scroll={{ y: `calc(100vh - 343px)` }} actionRef={tableRef} rowKey='id' params={tableDataFilterParams} request={(params) => getTableData(params)} /> )}
) }