import { connect } from "umi"; import type { CurrentUser } from "umi"; import type { ConnectState } from "@/models/connect"; import React, { useRef, useState } from "react"; import ProCard from "@ant-design/pro-card"; import { MenuFoldOutlined } from "@ant-design/icons"; import { Drawer, FormInstance } from "antd"; import { Button, message, Space, Spin, Tree } from "antd"; import useRequest from "@ahooksjs/use-request"; import { getServerpartTree, handleFormatNumber } from "@/services/options"; import type { ActionType } from "@ant-design/pro-table"; import ProTable from "@ant-design/pro-table"; import ReactHTMLTableToExcel from "react-html-table-to-excel"; import LeftSelectTree from "@/pages/reports/settlementAccount/component/leftSelectTree"; import PageTitleBox from "@/components/PageTitleBox"; import { handleGetAnnualAccountList, handleGetTableRevenueRecognition } from "../ShopExpenseDetail/service"; import moment from 'moment' import { wrapTreeNode } from "@/utils/format"; import YearExamineDetailTable from "../settlementAccount/component/YearExamineDetailTable"; import './index.less' import * as XLSX from 'xlsx'; import session from "@/utils/session"; const settlementSummaryTable: React.FC<{ currentUser: CurrentUser }> = (props) => { const { currentUser } = props const downloadBtnRef = useRef() const actionRef = useRef(); const formRef = useRef(); const [reqDetailList, setReqDetailList] = useState(); // 合计项数据源 const [printOut, setPrintOut] = useState(); // 打印数据的内容 const [collapsible, setCollapsible] = useState(false) const [treeView, setTreeView] = useState() const [printIndex, setPrintIndex] = useState(new Date().getTime()) let SETTLEMENT_MODESObj = session.get('SETTLEMENT_MODESObj') // 树相关的属性和方法 const [selectedId, setSelectedId] = useState() // 导出的加载效果 const [showLoading, setShowLoading] = useState(false) // 是否显示打印的表格 const [showExportTable, setShowExportTable] = useState(false) // 查询的条件 const [searchParams, setSearchParams] = useState() // 点击门店显示项目应收拆分数据 const [showDrawer, setShowDrawer] = useState() // 当前点击行的数据 const [currentRow, setCurrentRow] = useState() // 是否显示批量月度审批的按钮 const [isShowMonthBtn, setIsShowMonthBtn] = useState(false) const [columnsStateMap, setColumnsStateMap] = useState({ SettlementModes: { show: false }, SwitchDate: { show: false }, }); let settlementStateObj: any = { "9": "已结算", "0": "待结算", "-1": "提前退场", } const columns: any = [ { title: '统计日期', dataIndex: 'StatisticsYear', valueType: "date", hideInTable: true, hideInDescriptions: true, initialValue: moment().subtract('1', 'month'), // search: { // transform: (value: any) => { // return { // StartDate: moment(value[0]).format('YYYYMM'), // EndDate: moment(value[1]).format('YYYYMM'), // }; // }, // }, fieldProps: { picker: "year", format: 'YYYY' } }, { title: "结算状态", dataIndex: 'SettlementState', valueType: 'select', hideInTable: true, valueEnum: { "9": "已结算", "0": "待结算", "-1": "提前退场", } }, { title: "结算类型", dataIndex: 'SettlementType', valueType: 'select', valueEnum: { "1": "线上", "2": "线下" }, hideInTable: true }, { title: "结算模式", dataIndex: 'SettlementModes', valueType: 'select', hideInTable: true, valueEnum: SETTLEMENT_MODESObj, fieldProps: { mode: 'multiple', // 开启多选模式 }, initialValue: ["3000", "4000"] }, { title:
管理中心
, titleStr: '管理中心', dataIndex: 'SPREGIONTYPE_NAME', width: 200, hideInSearch: true, ellipsis: true, align: 'center' }, { title:
服务区
, titleStr: '服务区', dataIndex: 'SERVERPART_NAME', width: 200, hideInSearch: true, ellipsis: true, align: 'center' }, { title:
经营商户
, titleStr: '经营商户', dataIndex: 'MERCHANTS_NAME', width: 200, hideInSearch: true, ellipsis: true, align: 'center' }, { title:
门店
, titleStr: '门店', dataIndex: 'SERVERPARTSHOP_NAME', width: 150, hideInSearch: true, ellipsis: true, align: 'center', render: (_, record) => { return record?.SERVERPARTSHOP_NAME ? { console.log('record', record); setCurrentRow({ ...record, CLOSED_DATE: record?.ClosedDate, SHOPROYALTY_ID: record?.ShopRoyaltyId }) setShowDrawer(true) }}> {record?.SERVERPARTSHOP_NAME || ""} : "" } }, { title:
基础信息
, dataIndex: 'termInfo', titleStr: '基础信息', // fixed: 'left', hideInSearch: true, children: [ { title:
期数
, titleStr: '期数', dataIndex: 'IndexStr', width: 100, align: 'center', render: (_, record) => { return record?.Index > 0 ? `第${record?.Index}期` : "-" } }, { title:
时限
, titleStr: '时限', dataIndex: 'COMPACT_STARTDATECOMPACT_ENDDATE', width: 180, align: 'center', render: (_, record) => { // return record?.IndexDesc || '' return `${record?.COMPACT_STARTDATE || ''}-${record?.COMPACT_ENDDATE || ''}` } }, { title:
保底/固定租金
, titleStr: '保底/固定租金', exportType: "momeny", align: 'right', width: 140, valueType: 'digit', dataIndex: 'RENTFEE', render: (_, record) => { return record?.RENTFEE ? handleFormatNumber(record?.RENTFEE) : record?.RENTFEE === 0 ? '0.00' : '-' } }, { title:
提成比率
, titleStr: '提成比率', exportType: "rate", dataIndex: 'GUARANTEERATIO', align: 'center', width: 120, render: (_, record) => { return record?.GUARANTEERATIO ? `${record?.GUARANTEERATIO}%` : '' } }, { title:
结算模式
, dataIndex: 'SettlementModes', align: 'center', width: 120, valueType: 'select', valueEnum: SETTLEMENT_MODESObj, }, { title:
切换日期
, dataIndex: 'SwitchDate', align: 'center', width: 120, } ] }, { title:
营业额
, titleStr: '营业额', dataIndex: '', hideInSearch: true, children: [ { title:
现金
, titleStr: '现金', exportType: "momeny", align: 'right', width: 120, valueType: 'digit', dataIndex: 'CASHPAY_AMOUNT', render: (_, record) => { return record?.CASHPAY_AMOUNT ? handleFormatNumber(record?.CASHPAY_AMOUNT) : record?.CASHPAY_AMOUNT === 0 ? '0.00' : '-' } }, { title:
移动支付
, titleStr: '移动支付', exportType: "momeny", align: 'right', width: 120, valueType: 'digit', dataIndex: 'MOBILEPAY_AMOUNT', render: (_, record) => { return record?.MOBILEPAY_AMOUNT ? handleFormatNumber(record?.MOBILEPAY_AMOUNT) : record?.MOBILEPAY_AMOUNT === 0 ? '0.00' : '-' } }, { title:
小计
, titleStr: '小计', exportType: "momeny", align: 'right', valueType: 'digit', width: 120, dataIndex: 'REVENUEDAILY_AMOUNTTotal', render: (_, record) => { return record?.REVENUEDAILY_AMOUNTTotal ? handleFormatNumber(record?.REVENUEDAILY_AMOUNTTotal) : record?.REVENUEDAILY_AMOUNTTotal === 0 ? '0.00' : '-' } }, ] }, { title:
提成金额
, titleStr: '提成金额', exportType: "momeny", align: 'right', valueType: 'digit', hideInSearch: true, width: 120, dataIndex: 'GUARANTEERATIOAMOUNT', render: (_, record) => { return record?.GUARANTEERATIOAMOUNT ? handleFormatNumber(record?.GUARANTEERATIOAMOUNT) : record?.GUARANTEERATIOAMOUNT === 0 ? '0.00' : '-' } }, { title:
业主收款
, titleStr: '业主收款', exportType: "momeny", align: 'right', valueType: 'digit', hideInSearch: true, width: 120, dataIndex: 'BANKACCOUNT_AMOUNT', render: (_, record) => { return record?.BANKACCOUNT_AMOUNT ? handleFormatNumber(record?.BANKACCOUNT_AMOUNT) : record?.BANKACCOUNT_AMOUNT === 0 ? '0.00' : '-' } }, { title:
应收租金
, titleStr: '应收租金', exportType: "momeny", align: 'right', width: 120, hideInSearch: true, valueType: 'digit', dataIndex: 'RECEIVABLEAMOUNT', render: (_, record) => { return record?.RECEIVABLEAMOUNT ? handleFormatNumber(record?.RECEIVABLEAMOUNT) : record?.RECEIVABLEAMOUNT === 0 ? '0.00' : '-' } }, { title:
应收费用
, titleStr: '应收费用', exportType: "momeny", align: 'right', valueType: 'digit', hideInSearch: true, width: 120, dataIndex: 'SHOPEXPENSE_AMOUNT', render: (_, record) => { return record?.SHOPEXPENSE_AMOUNT ? handleFormatNumber(record?.SHOPEXPENSE_AMOUNT) : record?.SHOPEXPENSE_AMOUNT === 0 ? '0.00' : '-' } }, { title:
已缴费用
, titleStr: '已缴费用', exportType: "momeny", align: 'right', valueType: 'digit', hideInSearch: true, width: 120, dataIndex: 'PAID_AMOUNT', render: (_, record) => { return record?.PAID_AMOUNT ? handleFormatNumber(record?.PAID_AMOUNT) : record?.PAID_AMOUNT === 0 ? '0.00' : '-' } }, { title:
退补款
, titleStr: '退补款', exportType: "momeny", align: 'right', valueType: 'digit', hideInSearch: true, width: 120, dataIndex: 'REFUND_SUPPLEMENT', render: (_, record) => { return record?.REFUND_SUPPLEMENT ? handleFormatNumber(record?.REFUND_SUPPLEMENT) : record?.REFUND_SUPPLEMENT === 0 ? '0.00' : '-' } // fixed: 'right', }, { title:
结算月份
, titleStr: '结算月份', align: 'center', valueType: 'digit', hideInSearch: true, width: 120, dataIndex: 'STATISTICS_MONTH', render: (_, record) => { return record?.STATISTICS_MONTH ? moment(record?.STATISTICS_MONTH).format('MM') + '月' : '-' } }, { title:
是否结算
, titleStr: '是否结算', align: 'center', valueType: 'digit', hideInSearch: true, width: 120, dataIndex: 'settlementStateObjApprovalstate', render: (_, record) => { return record?.Approvalstate || record?.Approvalstate === 0 ? settlementStateObj[record?.Approvalstate.toString()] : '-' } }, { title:
备注(收退款)
, titleStr: '备注(收退款)', align: 'center', hideInSearch: true, width: 120, dataIndex: 'REFUND_SUPPLEMENTdesc', render: (_, record) => { return record?.REFUND_SUPPLEMENT > 0 ? '退款' : record?.REFUND_SUPPLEMENT <= 0 ? '收款' : '' } // REFUND_SUPPLEMENT 大于小于 }, ] // 导出方法 const handleExportDataToExcel = (isHaveChildren: boolean, isHaveTitle?: boolean) => { // isHaveChildren 判断表头是否有子集 console.log('1', reqDetailList); let columnsNoChildren: any = [] // 先创建需要显示的列表头 let headerArray: any = isHaveChildren ? [[], []] : [] // 需要合并单元格的内容 let mergesList: any = [] // 因为子集也拆出来了 这样实际的行数会比columns多 所以得知道多了多少 let moreColumns: number = 0 // 显示的列表有多少个 let showItemNumber: number = 0 let showIndex: number = 0 if (columns && columns.length > 0) { columns.forEach((item: any, index: number) => { if (!item.hideInTable) { showItemNumber += 1 if (item.children && item.children.length > 0) { mergesList.push({ s: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex }, e: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex + item.children.length - 1 }, }) moreColumns += item.children.length - 1 showIndex += item.children.length item.children.forEach((subItem: any) => { if (!subItem.hideInTable) { columnsNoChildren.push(subItem) headerArray[0].push(item.titleStr) headerArray[1].push(subItem.titleStr) } }) } else { columnsNoChildren.push(item) headerArray[0].push(item.titleStr) if (isHaveChildren) { headerArray[1].push(item.titleStr) mergesList.push({ s: { r: 0 + (isHaveTitle ? 1 : 0), c: showIndex }, e: { r: 1 + (isHaveTitle ? 1 : 0), c: showIndex }, }) } showIndex += 1 } } }) } console.log('columnsNoChildren', columnsNoChildren); console.log('headerArray', headerArray); console.log('mergesList', mergesList); // 先创建需要显示的列表头 // let headerArray = [ // ["管理中心", "服务区", "经营商户", "门店", "基础信息", "基础信息", "基础信息", "基础信息", "营业额", "营业额", "营业额", "提成金额", "业主收款", "应收租金", "应收费用", "已缴费用", "退补款", "结算月份", "是否结算", "备注(收退款)"], // ["管理中心", "服务区", "经营商户", "门店", "期数", "时限", "保底 / 固定租金", "提成比率", "现金", "移动支付", "小计", "提成金额", "业主收款", "应收租金", "应收费用", "已缴费用", "退补款", "结算月份", "是否结算", "备注(收退款)"] // ] // 创建一个工作簿 const ws = {} // 设置列宽:动态计算每列的最大内容长度 let colWidths: any = []; // 设置样式:对齐方式 const cellStyle: any = { alignment: { horizontal: 'right', vertical: 'right' } }; if (headerArray && headerArray.length > 0) { headerArray.forEach((row: any, rowIndex: any) => { row.forEach((cell: any, colIndex: any) => { const cellRef = XLSX.utils.encode_cell({ r: (isHaveChildren ? rowIndex : rowIndex) + (isHaveTitle ? 1 : 0), c: colIndex }); // ws[cellRef] = cell; console.log('cellRef', cellRef); ws[cellRef] = { v: cell, t: 's', s: cellStyle }; const cellLength = getCharWidth((cell && cell.toString()) || ''); if (!colWidths[colIndex]) { colWidths[colIndex] = cellLength; } else { colWidths[colIndex] = Math.max(colWidths[colIndex], cellLength); } }) }) } // 设置工作表的范围(必需) const range = { s: { r: 0, c: 0 }, // 开始行列 e: { r: reqDetailList.length + (isHaveChildren ? 2 : 1) + (isHaveTitle ? 1 : 0), c: columnsNoChildren.length } // 结束行列 }; ws["A1"] = { v: `(${searchParams?.StatisticsYear})年度结算汇总表`, t: 's', s: { font: { bold: true, // 字体加粗 color: { rgb: "#000" }, // 字体颜色红色 sz: 20 // 字号为14 }, alignment: { horizontal: 'center', // 水平居中 vertical: 'center' // 垂直居中 }, } }; if (isHaveTitle) { mergesList.push( { s: { r: 0, c: 0 }, e: { r: 0, c: columnsNoChildren.length }, } ) } ws['!merges'] = mergesList ws['!ref'] = XLSX.utils.encode_range(range); // 设置单元格范围 if (reqDetailList && reqDetailList.length > 0) { reqDetailList.forEach((item: any, index: number) => { columnsNoChildren.forEach((subItem: any, subIndex: number) => { for (let key in item) { if (subItem.dataIndex === key) { let res = item[key] const cellRef = XLSX.utils.encode_cell({ r: (isHaveChildren ? index + 2 : index + 1) + (isHaveTitle ? 1 : 0), c: subIndex }); // ws[cellRef] = cell; let newRes = res ? subItem.exportType === 'momeny' ? handleFormatNumber(res) : subItem.exportType === 'rate' ? `${res}%` : res : subItem.exportType === 'momeny' ? '0' : "" ws[cellRef] = { v: newRes, t: 's', s: { alignment: { horizontal: 'center', // 水平居中 vertical: 'center' // 垂直居中 } } }; const cellLength = getCharWidth((newRes && newRes.toString()) || ''); if (!colWidths[subIndex]) { colWidths[subIndex] = cellLength; } else { if (subIndex === 2) { console.log('colWidths[subIndex] ', colWidths[subIndex]); console.log('cellLength.length', cellLength); } colWidths[subIndex] = Math.max(colWidths[subIndex], cellLength); } } } }) }) } ws['!cols'] = colWidths.map((width: any) => ({ wch: width + 2 })); // wch 表示字符宽度 console.log('ws', ws); // array.forEach((row: any, rowIndex: any) => { // row.forEach((cell: any, colIndex: any) => { // // 通过设置单元格的数值和样式,保留原始格式 // const cellRef = XLSX.utils.encode_cell({ r: rowIndex, c: colIndex }); // 跳过表头行 // console.log('cellRef', cellRef); // if (typeof cell === 'number' && cell.toFixed) { // // 如果是数字,保持两位小数 // ws[cellRef].z = '0.00'; // 设置数字格式为两位小数 // } // }); // }); // 创建工作簿 const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Sheet1'); // 导出 Excel 文件 XLSX.writeFile(wb, `(${searchParams?.StatisticsYear})年度结算汇总表.xlsx`); setShowLoading(false) }, // 判断字符类型的辅助函数 getCharWidth = (cell: any) => { if (!cell) { return '' } if (typeof cell === 'number') { return cell.toString().length > 150 ? 150 : cell.toString().length; // 数字通常和英文字符宽度相同 } else if (/[^\x00-\xff]/.test(cell)) { return cell.length * 1.8 > 300 ? 300 : cell.length * 1.8; // 中文字符宽度大约是英文字符的2倍 } else { return cell.length > 150 ? 150 : cell.length; // 英文字符的宽度为1 } } const exportTable = (e) => { e.stopPropagation(); // 防止Collapse组件收起 const main = document.getElementsByClassName(`settlementSummaryTable${printIndex}`)[0] const thead = main.querySelector('thead').cloneNode(true); // 深克隆DOM节点 const tbody = main.querySelector('tbody').cloneNode(true); // 深克隆DOM节点 const container = document.querySelector('#hiddenBox'); // 加个标题 const tableTop = document.createElement('div') tableTop.innerText = `(${searchParams?.StatisticsYear})年度结算汇总表` tableTop.setAttribute('style', 'font-size:20px;font-weight:600;display:flex;width:100%;justify-content: center;text-align: center;'); const tempTable = document.createElement('table'); tempTable.appendChild(tableTop) tempTable.appendChild(thead); tempTable.appendChild(tbody); tempTable.setAttribute('id', 'table-to-xls-settlementSummaryTable'); // 给table添加id,值与按钮上的table字段对应 container.appendChild(tempTable); // 把创建的节点添加到页面容器中 setShowLoading(false) downloadBtnRef.current.handleDownload(); setShowExportTable(false) tempTable.remove() // 防止重复打印一个内容 } return (
{ // 打印报表 if (!reqDetailList || reqDetailList.length === 0) return; setPrintOut(el); }} > { showLoading ?
数据导出中...
: '' }
{ showExportTable && reqDetailList && reqDetailList.length > 0 ? : '' }
{ return `${record?.SPREGIONTYPE_ID}-${record?.SERVERPART_ID}-${record?.SERVERPARTSHOP_ID}-${record?.BUSINESSPROJECT_ID}-${record?.MERCHANTS_ID}-${record?.IndexStr}` }} headerTitle={} search={{ span: 6 }} scroll={{ x: "max-content", y: 'calc(100vh - 450px)' }} request={async (params) => { console.log('params', params); if (!selectedId || !params?.StatisticsYear) { return } const req: any = { StatisticsYear: params?.StatisticsYear, ServerpartId: selectedId, SettlementState: params?.SettlementState, SettlementModes: params?.SettlementModes, SettlementType: params?.SettlementType // ServerpartIds: selectedId, // // ShopRoyaltyId: compareCurrent?.ShopRoyaltyId, // // BusinessProjectId: compareCurrent?.BUSINESSPROJECT_ID, // startDate: params?.StartDate || '', // endDate: params?.EndDate || '', // SolidType: true } setSearchParams(params) console.log('req', req); const data = await handleGetAnnualAccountList(req) let res: any = [] let exportData: any = [] if (data && data.length > 0) { res = data } console.log('res', res); if (res && res.length > 0) { let exportRes = JSON.parse(JSON.stringify(res)) exportRes.forEach((item: any, index: number) => { item.index = `${index + 1}` // item.SPREGIONTYPE_NAME = item.SPREGIONTYPE_NAME.split('管理中心') if (item.children && item.children.length > 0) { item.children.forEach((subItem: any, subIndex: number) => { subItem.index = `${index + 1}-${subIndex + 1}` // subItem.SPREGIONTYPE_NAME = subItem.SPREGIONTYPE_NAME.split('管理中心') // subItem.SERVERPART_NAME = subItem.SERVERPART_NAME.split('服务区') if (subItem.children && subItem.children.length > 0) { subItem.children.forEach((thirdItem: any, thirdIndex: number) => { thirdItem.index = `${index + 1}-${subIndex + 1}-${thirdIndex + 1}` thirdItem.SPREGIONTYPE_NAME = thirdItem.SPREGIONTYPE_NAME.split('管理中心')[0] thirdItem.SERVERPART_NAME = thirdItem.SERVERPART_NAME.split('服务区')[0] thirdItem.COMPACT_STARTDATECOMPACT_ENDDATE = `${thirdItem?.COMPACT_STARTDATE || '-'}-${thirdItem?.COMPACT_ENDDATE || '-'}` thirdItem.settlementStateObjApprovalstate = thirdItem?.Approvalstate || thirdItem?.Approvalstate === 0 ? settlementStateObj[thirdItem?.Approvalstate.toString()] : '-' thirdItem.REFUND_SUPPLEMENTdesc = thirdItem?.REFUND_SUPPLEMENT > 0 ? '退款' : thirdItem?.REFUND_SUPPLEMENT <= 0 ? '收款' : '' exportData.push(thirdItem) }) } }) } }) console.log('exportData', exportData); setReqDetailList(exportData) return { data: res, success: true } } setReqDetailList(exportData) return { data: [], success: true } }} toolbar={{ actions: [ , ] }} columnsState={{ value: columnsStateMap, onChange: setColumnsStateMap, }} />
{ setShowDrawer(false) setCurrentRow(undefined) }} bodyStyle={{ backgroundColor: "#fff", padding: 0 }} closable={false} className="summaryDrawer" >
) } export default connect(({ user }: ConnectState) => ({ currentUser: user.currentUser }))(settlementSummaryTable);