/* * @Author: zzy 411037547@qq.com * @Date: 2022-05-24 16:03:36 * @LastEditors: cclu 1106109051@qq.com * @LastEditTime: 2024-12-16 11:06:29 * @FilePath: \cloud-platform\src\pages\reports\Contract\index.tsx * @Description: 合同应收预警模块 */ import moment from 'moment'; import numeral from 'numeral'; import ReactToPrint from 'react-to-print'; import ProTable from '@ant-design/pro-table'; import ProCard from '@ant-design/pro-card'; import useRequest from "@ahooksjs/use-request"; import SubMenu from 'antd/lib/menu/SubMenu'; import { PageContainer } from '@ant-design/pro-layout'; import { MenuFoldOutlined } from '@ant-design/icons'; import { fmoney, getServerpartTree } from '@/services/options'; import { contractType } from '@/pages/contract/emun'; import { connect, history } from 'umi'; import { useEffect, useRef, useState } from 'react'; import { Avatar, Button, Descriptions, Drawer, Menu, message, Space, Statistic, Typography } from 'antd'; import type { ColumnsState } from '@ant-design/pro-table'; import type { ConnectState } from '@/models/connect'; import type { ProFormInstance } from '@ant-design/pro-form'; import type { ActionType, ProColumns } from '@ant-design/pro-table'; import type { CurrentUser } from '@/models/user'; import type { RevenueConfirmModel, RevenueConfirmParams } from './data'; import type { BusinessProjectModel } from '@/pages/BussinessProject/data'; import { getRevenueConfirmList } from './service'; import { printContract, handlePrint } from "@/utils/utils"; import { getProjectDetail } from '@/pages/BussinessProject/service'; import { exportExcel, printOutBody } from '@/utils/utils'; import RevenueList from '@/pages/BussinessProject/components/RevenueList'; import '@/pages/merchantManagement/style.less'; import session from "@/utils/session"; import { getDetail } from "@/pages/basicManage/Serverpart/service"; import PageTitleBox from '@/components/PageTitleBox'; const { Text } = Typography; // 标题金额拆分小数点前后显示大小 const amountDom = (value: any) => { const stringValue = `${value}` const [intValue, floatValue] = stringValue.split(".") return floatValue ? <>{numeral(intValue || 0).format("0,0")}.{floatValue} : <>{intValue} } /** * @description: 合同应收预警报表页面 * @param {currentUser} 当前登录用户信息 * @return { React.FC } 页面主体 */ const ShareBenefit: React.FC<{ currentUser?: CurrentUser }> = (props) => { const { currentUser } = props const actionRef = useRef() // 表格对应的对象 const [showDetail, setShowDetail] = useState(false) // 是否显示详情 const [currentRow, setCurrentRow] = useState(undefined) // 选中的当前行 const [selectShops, setSelectShops] = useState() // 选中的数量 const [curProject, setProject] = useState(undefined) // 选中的当前行 const [printOut, setPrintOut] = useState(); // 打印数据的内容 const [currentServiceName, setServiceName] = useState();// 拿到服务区的名字还为截取 // 树相关的属性和方法 const [currenMenu, setCurrenMenu] = useState(); // 当前选中的左侧菜单 const [selectedId, setSelectedId] = useState([]) const [collapsible, setCollapsible] = useState(false) // 选中的服务区名称 const [serviceName, setSelectServiceName] = useState() // 加载服务区树 const { loading: treeLoading, data: treeView = [] } = useRequest(() => { return getServerpartTree(currentUser?.ProvinceCode) }) // 表格数据 const [tableData, setTableData] = useState()// 表格数据 // 根据左侧选中的菜单加载右侧数据 const loadSelectedId = async (item?: any) => { setSelectedId(item.selectedKeys || []) // 选中的子菜单key const [type, value] = item.key.split('-') if (type === '1' && item.keyPath) { const newCurrentMenu = item.keyPath[0].split('-').length > 1 ? item.keyPath[0].split('-')[1] : item.keyPath[0] setCurrenMenu(newCurrentMenu) const service = await getDetail(newCurrentMenu); setServiceName(service.SERVERPART_NAME) } else { const newCurrentMenu = value // 父级菜单新老相等 并且触发点击为 点击父级菜单 if (newCurrentMenu === currenMenu && type === '0') { return } setCurrenMenu(newCurrentMenu) } actionRef?.current?.reload() } // 生成左侧菜单 const getMenuDom = (data: any[], callback: (item: any) => void) => { return (data.map((element: any) => { if (element.children && element.children.length > 0) { return ( : null} key={`${element.key || element.value}`} onTitleClick={(item) => { // 选中一级菜单 if (!currenMenu || item.key !== `${currenMenu?.key}`) { callback.call(callback, item) } item.domEvent.stopPropagation(); }} > {element.children && element.children.length > 0 && getMenuDom(element.children, callback)} ) } return ( : null} key={`${element.key || element.value}`}>{element.label}) })) } const revenuenColumns: ProColumns[] = [ { title: '合作单位', dataIndex: 'MERCHANTS_NAME', align: 'center', hideInSearch: true, }, { title: '统计时间', dataIndex: 'search_date', valueType: 'dateRange', hideInTable: true, colSize: 2, initialValue: [moment().add(-1, 'day').format('YYYY-01-01'), moment().add(-1, 'day').format('YYYY-MM-DD')], search: { transform: (value) => { return { StartDate: value[0], EndDate: value[1], }; }, }, }, { title: '开始日期', dataIndex: 'BUSINESS_STARTDATE', valueType: "date", width: 110, align: 'center', hideInSearch: true, }, { title: '结束日期', dataIndex: 'BUSINESS_ENDDATE', valueType: "date", width: 110, align: 'center', hideInSearch: true, }, { title: '经营模式', dataIndex: 'BUSINESS_TYPE', valueType: 'select', valueEnum: contractType, width: 110, align: "center", hideInTable: true, initialValue: '1000' }, { title: '营业额', dataIndex: 'ACTUAL_REVENUE', valueType: "money", width: 140, align: "center", hideInSearch: true, render: (_, record) => { return { // 查询经营项目信息详情 // const project = await getProjectDetail(record.BUSINESSPROJECT_ID); getProjectDetail(record.BUSINESSPROJECT_ID).then((project) => { setProject({ ...project, BUSINESSPROJECT_ID: project.BUSINESSPROJECT_ID }) }) // 点击实际营业额时 打开抽屉 展示每日营收数据 setCurrentRow(record) setShowDetail(true) }}>{record.ACTUAL_REVENUE ? fmoney(record.ACTUAL_REVENUE, 2) : ''} }, }, { title: '提成', dataIndex: 'GUARANTEERATIO', valueType: 'digit', align: "center", width: 110, hideInSearch: true, render: (_, record) => { return `${record.GUARANTEERATIO}%` }, }, { title: '提成额', dataIndex: 'PARTYA_SHAREPROFIT', valueType: "money", align: "center", width: 140, hideInSearch: true }, { title: '保证租金', dataIndex: 'GUARANTEE_AMOUNT', valueType: "money", width: 140, align: "center", hideInSearch: true }, { title: '备注说明', dataIndex: 'REVENUECONFIRM_DESC', hideInSearch: true, render: () => { return '' }, } ] return (
{ // 打印报表 setPrintOut(el); }}> { setCollapsible(!collapsible) }} />} colSpan={!collapsible ? "300px" : "60px"} title={!collapsible ? "请选择服务区" : ""} headerBordered collapsed={collapsible} > {!treeLoading && { // 拿到选择的服务区名称给导出的文件赋值 if (treeView && treeView.length > 0) { treeView.forEach((i: any) => { if (i.children && i.children.length > 0) { i.children.forEach((subItem: any) => { if (subItem.key === item.key) { setSelectServiceName(subItem.label) } }) } }) } loadSelectedId(item) }} > {getMenuDom(treeView, loadSelectedId)} } cardProps={{ // 去除表格内边距 bodyStyle: { padding: 24 } }} bordered={true} rowKey="REVENUECONFIRM_ID" headerTitle={} actionRef={actionRef} search={{ span: 6, labelWidth: 'auto' }} // manualRequest={true} // 是否需要手动触发首次请求, 配置为 true 时不可隐藏搜索表单 request={async (pramas, sorter) => { const sortstr = Object.keys(sorter).map(n => { const value = sorter[n] return value ? `${n} ${value.replace('end', '')}` : '' }) const data = await getRevenueConfirmList({ ServerpartId: selectedId && selectedId.length > 0 ? selectedId[0].split('-')[1] : currenMenu || '0', StartDate: pramas.StartDate || '', EndDate: pramas.EndDate || '', sortstr: sortstr.toString() } as RevenueConfirmParams ) setTableData(data.data) return { ...data, data: data.data.map((n: RevenueConfirmModel) => { return { ...n, shopids: n.SERVERPARTSHOP_ID ? n.SERVERPARTSHOP_ID.split(',') : [] } }) } }} options={false} columns={revenuenColumns} pagination={false} rowSelection={{ onChange: (selectedRowKeys, selectedRows) => { setSelectShops(selectedRows) } }} toolbar={{ actions: [ // react打印插件 ( )} // 点击触发的事件 content={() => { // printOut是整个页面的dom 一般情况都是有的 if (printOut) { // 标题 先找到标题的dom元素 const title = document.createElement('p') // 设置标题dom元素的内容 title.innerHTML = `${currentServiceName}合作单位保底提成结算表` // 给标题dom设置样式 title.setAttribute('style', 'font-size:20px;font-weight:600;display:flex;width:100%;justify-content: center;') // 日期时间 timeUnit为父节点 time和date为子节点 显示打印内容标题下面 表格上面的那一块区域 const timeUnit = document.createElement('div') const time = document.createElement('div') const date = document.createElement('div') // 时间显示的内容 time.innerHTML = `日期: ${moment().format('YYYY年MM月DD日')}` date.innerHTML = `单位: 元` // 加入到父节点中去 timeUnit.appendChild(time) timeUnit.appendChild(date) // 样式 timeUnit.setAttribute('style', 'width:100%;display:flex;justify-content: space-between;margin-bottom:15px;') // 数据内容 打印的表格dom // 表格的dom元素 const dataList = document.getElementsByClassName('ant-table-content') // 克隆出来不影响原页面 const domNoChange = dataList[0].cloneNode(true) // 拿到是不是有已经被选中的行 const length = selectShops ? selectShops.length : false if (length) { // 拿到表头的dom const tableHeader = domNoChange.getElementsByClassName('ant-table-thead') // 表头增加样式 tableHeader[0].setAttribute('style', 'width:100%;background: #fafafa;') // 拿到每一列的标题 const th = tableHeader[0].getElementsByTagName("th"); // 由于页面的表头和效果图要的不一样 所以就自定义一个效果图上要的表头数组 // 由于这个页面莫名其妙在每行的后面都有一个空节点,所以最后给他赋值个空的 const titleText = ['合作单位', '结算起止时间', '营业额', '提成标准', '提成额', '保证租金', '备注说明', ''] const headList: { innerHTML: string; }[] = [] // th的第一个节点删除是因为要把选择框的节点去掉 打印不能有选择框出现 th.forEach((item: { innerHTML: string; }, index: string | number) => { if (index === 0) { item.remove() } else if (index <= 7 && index !== 0) { // 页面需要的节点 给他赋值上需要的标题 然后添加到一个数组里面 item.innerHTML = titleText[index] headList.push(item) } }) // 不让标题内容换行 minWidth无法适配 只能不换行来满足需求 th.forEach((item: { innerHTML: string; }, index: string | number) => { // 因为最后一个节点还是空节点所以不让最后一个不换行 其实问题也不大 if (index !== th.length - 1) { item.style.whiteSpace = 'nowrap' } }) // 表单打印的数据内容 // 表单的数据节点 const tableBody = domNoChange.getElementsByClassName('ant-table-tbody') // 每一行数据 const tr = tableBody[0].getElementsByTagName('tr') // 哪几行是没被选中的索引列表 const numList: any[] = [] // 遍历每一行数据 tr.forEach((item: { getElementsByClassName: (arg0: string) => any; }, index: any) => { // 拿到打钩的那个节点 如果这个节点下的checked节点为true那就是被选中了 false就是没选中 const isChecked = item.getElementsByClassName('ant-checkbox-input') // 没被选中的行数的索引被添加到数组中 if (!isChecked[0].checked) { numList.push(index) } }) // 倒序的for循环 正序的话当一个节点删除时 下一个节点会到刚刚删除的节点位置 无法参与判断 // 如果是没选中的节点就直接移除掉 留下来的节点都是选中的节点 for (let i = tr.length - 1; i >= 0; i -= 1) { numList.forEach(item => { if (i === item) { tr[i].remove() } }) } // 选中的节点继续遍历 tr.forEach((item: { getElementsByClassName: (arg0: string) => any; }, index: any) => { // 因为页面的dom元素和要求图的dom元素不一样 所以用list先存页面中的数据 const list: any[] = [] // 获得每一行的 每一列对应的节点 const td = item.getElementsByTagName('td') // 遍历一行的数据 td.forEach((subItem: { innerHTML: any; }, subIndex: number) => { // 背景都变成白色 subItem.style.backgroundColor = 'white' // 因为td的第一项是选择框 所以不放进数据列表中 if (subIndex >= 1) { list.push(subItem.innerHTML) } }) // 把选择框的dom节点移除 td[0].remove() // 拼接数据 td.forEach((subItem: { innerHTML: string; }, subIndex: number) => { subItem.style.backgroundColor = 'white' if (subIndex === 0) { subItem.innerHTML = list[0] subItem.style.whiteSpace = 'nowrap' } else if (subIndex === 1) { // 时间数据的拼接 subItem.innerHTML = `${moment(list[1]).format('YYYY年MM月DD日')} - ${moment(list[2]).format('YYYY年MM月DD日')}` subItem.style.whiteSpace = 'nowrap' } else if (subIndex > 1 && subIndex <= 6) { // 因为上面拼接了一个 所以索引加1 小于6 是因为 要求的数据只有七行 但是会有一个空节点 subItem.innerHTML = list[subIndex + 1] if (subIndex !== 6) { // 备注就让它可以换行 subItem.style.whiteSpace = 'nowrap' } } else { subItem.style.backgroundColor = 'white' } }) }) } else { // 没有选中任何一行 就默认打印全部 // 表头dom节点 逻辑和上面差不多 就是没有了是否选中的判断 const tableHeader = domNoChange.getElementsByClassName('ant-table-thead') const th = tableHeader[0].getElementsByTagName("th"); const titleText = ['合作单位', '结算起止时间', '营业额', '提成标准', '提成额', '保证租金', '备注说明', ''] const headList: { innerHTML: string; }[] = [] th[0].remove() th.forEach((item: { innerHTML: string; }, index: string | number) => { if (index !== th.length - 1) { item.style.whiteSpace = 'nowrap' } if (index <= 7 && index >= 0) { item.innerHTML = titleText[index] headList.push(item) } else { item.style.maxWidth = '0px' } }) // 数据内容 const tableBody = domNoChange.getElementsByClassName('ant-table-tbody') const tr = tableBody[0].getElementsByTagName('tr') // 先遍历每一行的数据 拿到每一行中的数据 tr.forEach((item: { getElementsByTagName: (arg0: string) => any; }, index: any) => { const list: any[] = [] const td = item.getElementsByTagName('td') // 再对每一行的数据进行遍历 把原本的值先存一份 td.forEach((subItem: { innerHTML: any; }, subIndex: number) => { if (subIndex >= 1) { list.push(subItem.innerHTML) } }) td[0].remove() td.forEach((subItem: { innerHTML: string; }, subIndex: number) => { if (subIndex === 0) { subItem.innerHTML = list[0] subItem.style.whiteSpace = 'nowrap' } else if (subIndex === 1) { subItem.innerHTML = `${moment(list[1]).format('YYYY年MM月DD日')} - ${moment(list[2]).format('YYYY年MM月DD日')}` subItem.style.whiteSpace = 'nowrap' } else if (subIndex > 1 && subIndex <= 6) { subItem.innerHTML = list[subIndex + 1] if (subIndex !== 6) { subItem.style.whiteSpace = 'nowrap' } } else { item.setAttribute('style', 'width: 0%') } }) }) } // 表单底部签字盖章数据 // 创建一个父节点 下面三个子节点 然后flex布局变掉 加一个当前用户的名称就可以 const currentUser = session.get('currentUser'); const signAndSeal = document.createElement('div') signAndSeal.setAttribute('style', 'width:100%;display:flex;align-item:center;justify-content: space-between;margin-top:20px') const serviceSeal = document.createElement('div') const companySeal = document.createElement('div') const makeExcel = document.createElement('div') serviceSeal.innerHTML = '服务区 (签字盖章) :' companySeal.innerHTML = '合作单位 (签字盖章) :' makeExcel.innerHTML = `制表人 : ${currentUser.Name}` makeExcel.setAttribute('style', 'text-align:right') signAndSeal.appendChild(serviceSeal) signAndSeal.appendChild(companySeal) signAndSeal.appendChild(makeExcel) // printContract方法也就是把第一个形参数组里面的每一项拼到一个父节点的里面 然后return出来 const ele = printContract([title || '', timeUnit || '', domNoChange || '', signAndSeal || ''], ''); // content方法中return出来的值 就会自动调起ReactToPrint依赖 然后根据ele来打印 return ele } // 拿不到整个页面dom就输出空 但是一般不会拿不到 return '' }} />, // ( // // )} // content={() => { // // printOut是整个页面的dom 一般情况都是有的 // if (printOut){ // const ele = handlePrint(printOut) // return ele // } // return '' // }} // /> ] }} /> {/* 查看项目详情 右侧弹出的抽屉 */} { // 关闭抽屉 设置抽屉状态为关闭 setShowDetail(false); setProject(undefined); setCurrentRow(undefined); }} bodyStyle={{ backgroundColor: "#f9f9f9", padding: 0 }} closable={false} > {/* 抽屉打开时 加载项目详情组件 */} {showDetail && }
); } export default ShareBenefit;