ylj20011123 02b1425689 update
2025-09-15 11:29:35 +08:00

449 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

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.

// 历史销售单品报表
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 type { FormInstance } from "antd";
import { Button, message, Space, Spin, Tree } from "antd";
import useRequest from "@ahooksjs/use-request";
import { getFieldEnumTree, getServerpartTree, getSPRegionShopTree, handleCallLogs, handleGetServiceShopTreeList } 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 {
handleGetCommoditySaleSummary,
} from "@/pages/reports/BusinessAnalysis/saleHisReport/service";
import moment from "moment";
import session from "@/utils/session";
import { handleGetBusinessTrades } from "../saleReport/service";
import ProDescriptions from "@ant-design/pro-descriptions";
import numeral from "numeral";
import PageTitleBox from "@/components/PageTitleBox";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const saleHisReport: React.FC<{ currentUser: CurrentUser }> = (props) => {
const { currentUser } = props
const downloadBtnRef = useRef<any>()
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const [reqDetailList, setReqDetailList] = useState<any>(); // 合计项数据源
const [printOut, setPrintOut] = useState<any>(); // 打印数据的内容
const [collapsible, setCollapsible] = useState<boolean>(false)
const [treeView, setTreeView] = useState<any>()
const [isFirst, setIsFirst] = useState<boolean>(true)
// 加载服务区树
const { loading: treeLoading, data: treeViews } = useRequest(async () => {
const data = await handleGetServiceShopTreeList({
ProvinceCode: currentUser?.ProvinceCode,
BusinessState: "1000,2000,3000",
ShowState: true,
SortStr: 'BUSINESS_STATE,SHOPSHORTNAME'
});
setTreeView(data)
return data
})
// 树相关的属性和方法
const [selectedId, setSelectedId] = useState<string>()
// 导出的加载效果
const [showLoading, setShowLoading] = useState<boolean>(false)
// 是否显示打印的表格
const [showExportTable, setShowExportTable] = useState<boolean>(false)
const businessTypeObj = session.get("BUSINESSTYPEObj")
const [printIndex, setPrintIndex] = useState<number>(new Date().getTime())
// 商品业态列表
const [commodityFormatsObj, setCommodityFormatsObj] = useState<any>()
const columns: any = [
{
title: '统计月份',
dataIndex: 'search_months',
valueType: "dateRange",
hideInTable: true,
colSize: 1,
hideInDescriptions: true,
initialValue: [moment().add(-2, 'month').endOf("month"), moment().add(-2, 'month').endOf("month")],
search: {
transform: (value) => {
return {
StartTime: value[0].replace('-', ''),
EndTime: value[1].replace('-', ''),
};
},
},
fieldProps: {
picker: "month",
format: 'YYYY-MM',
disabledDate: (current: any) => current && current > moment().startOf("month")
}
},
{
title: '查询',
dataIndex: 'SearchKeyValue',
hideInDescriptions: true,
hideInTable: true,
colSize: 1,
fieldProps: {
placeholder: '请输入商品条码/商品名称'
}
},
// {
// title: '商品名称',
// dataIndex: 'commodityName',
// hideInDescriptions: true,
// hideInTable: true,
// colSize: 1,
// fieldProps: {
// placeholder: '请输入商品名称'
// }
// },
// {
// title: '商品条码',
// dataIndex: 'commodityBarcode',
// hideInDescriptions: true,
// hideInTable: true,
// colSize: 1,
// fieldProps: {
// placeholder: '请输入商品条码'
// }
// },
{
title: '商品业态',
dataIndex: 'shopTrade',
hideInTable: true,
valueType: 'select',
request: async () => {
const data = await getFieldEnumTree({ FieldExplainField: 'BUSINESSTYPE' })
console.log('data', data)
return data
}
},
{
title: '经营模式',
dataIndex: 'BusinessType',
valueType: 'select',
valueEnum: businessTypeObj,
hideInTable: true,
},
{
title: '序号',
width: 80,
dataIndex: 'index',
valueType: 'index',
align: 'center',
hideInSearch: true,
},
{
title: '商品名称',
width: 200,
ellipsis: true,
align: 'center',
dataIndex: 'Commodity_Name',
hideInSearch: true,
},
{
title: '商品条码',
width: 150,
ellipsis: true,
align: 'center',
dataIndex: 'Commodity_Barcode',
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
width: 150,
align: 'right',
valueType: 'digit',
dataIndex: 'Commodity_RetailPrice',
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'Total_Count',
hideInSearch: true,
width: 150,
align: 'right',
valueType: 'digit',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'Total_OffAmount',
valueType: 'digit',
hideInSearch: true,
width: 150,
align: 'right',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'Total_SellAmount',
valueType: 'digit',
hideInSearch: true,
width: 150,
align: 'right',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'Commodity_PurchasePrice',
valueType: 'digit',
hideInSearch: true,
width: 150,
align: 'right',
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: 'Total_SellAmountRate',
hideInSearch: true,
width: 150,
align: 'right',
render: (_, record) => {
return record?.Total_SellAmountRate ? `${record?.Total_SellAmountRate}%` : '-'
}
},
]
const exportTable = (e) => {
e.stopPropagation(); // 防止Collapse组件收起
const main = document.getElementsByClassName(`saleHisReportHideBox${printIndex}`)[0]
const thead = main.querySelector('thead').cloneNode(true); // 深克隆DOM节点
const tbody = main.querySelector('tbody').cloneNode(true); // 深克隆DOM节点
const container = document.querySelector('#hiddenBox');
const tempTable = document.createElement('table');
tempTable.appendChild(thead);
tempTable.appendChild(tbody);
tempTable.setAttribute('id', 'table-to-xls-saleHisReport'); // 给table添加id值与按钮上的table字段对应
container.appendChild(tempTable); // 把创建的节点添加到页面容器中
setShowLoading(false)
downloadBtnRef.current.handleDownload();
setShowExportTable(false)
tempTable.remove() // 防止重复打印一个内容
}
// 查询的条件
const [searchParams, setSearchParams] = useState<any>()
return (
<div ref={(el) => {
// 打印报表
if (!reqDetailList || reqDetailList.length === 0) return;
setPrintOut(el);
}} >
{
showLoading ?
<div
style={{
width: '100%',
height: '100%',
background: 'rgba(0,0,0,0.1)',
position: 'fixed',
zIndex: 5,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '15px 20px 10px',
background: '#fff',
borderRadius: '8px',
width: '200px'
}}>
<Spin />
<span style={{ marginLeft: '5px' }}>...</span>
</div>
</div> : ''
}
<div className={`saleHisReportHideBox${printIndex}`} style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }}>
{
showExportTable && reqDetailList && reqDetailList.length > 0 ?
<ProTable
columns={columns}
dataSource={reqDetailList}
pagination={false}
expandable={{
defaultExpandAllRows: true
}}
/> : ''
}
</div>
<div id='hiddenBox' style={{ position: 'fixed', zIndex: -1, top: 0, left: 0 }} />
<div style={{ backgroundColor: '#fff', display: 'flex' }}>
<ProCard
style={{ width: !collapsible ? "300px" : "60px" }}
className="pageTable-leftnav"
bodyStyle={{ padding: 0, paddingTop: 20, paddingLeft: 20, width: !collapsible ? "300px" : "60px" }}
extra={<MenuFoldOutlined onClick={() => {
setCollapsible(!collapsible)
}} />}
colSpan={!collapsible ? "300px" : "60px"}
title={!collapsible ? "请选择服务区" : ""}
headerBordered
collapsed={collapsible}
>
{treeView && treeView.length > 0 ? <Tree
checkable
treeData={[{
label: '全部',
value: 0,
key: '0-0',
children: treeView
}]}
fieldNames={{
title: "label",
key: "key"
}}
blockNode
defaultExpandedKeys={['0-0']}
onCheck={(checkedKeys: React.Key[] | any, info) => {
const selectedIds = info.checkedNodes.filter(n => n?.type === 2)
setSelectedId(selectedIds.map(n => n?.value)?.toString() || '')
// actionRef?.current?.reload()
// getData(selectedIds.map(n => n?.value)?.toString() || '')
}}
// switcherIcon={<PlusOutlined />}
/> : ''}
</ProCard>
<div style={{
width: !collapsible ? 'calc(100% - 300px)' : 'calc(100% - 60px)',
paddingTop: 0,
paddingBottom: 0,
paddingRight: 0
}}>
<ProTable
actionRef={actionRef}
formRef={formRef}
columns={columns}
bordered
headerTitle={<PageTitleBox props={props} />}
scroll={{ y: 'calc(100vh - 540px)' }}
search={{ span: 6 }}
pagination={{ pageSize: 100 }}
request={async (params) => {
if (isFirst) {
setIsFirst(false)
return
}
if (!selectedId) {
message.error('请选择服务区')
return
}
console.log('params', params)
setSearchParams(params)
handleCallLogs()
const req = {
...params,
ServerpartShopIds: selectedId,
DataType: 2
// shopTrade:'',
// businessType:''
}
console.log('req', req)
const data = await handleGetCommoditySaleSummary(req)
console.log('data', data)
let list: any = []
if (data && data.length > 0) {
data.forEach((item: any, index: number) => {
item.index = index + 1
})
list = data
}
setReqDetailList(list)
return { data: list, success: true }
}}
toolbar={{
actions: [
<span style={{ visibility: 'hidden' }}>
<ReactHTMLTableToExcel
buttonText={'导出excel'}
ref={downloadBtnRef}
table="table-to-xls-saleHisReport"
filename={`单品统计报表${searchParams?.StartTime}-${searchParams?.EndTime}`}
sheet="sheet1"
/>
</span>,
<Button
key="new"
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(columns,
reqDetailList,
`历史销售单品报表${searchParams?.StartTime}-${searchParams?.EndTime}`,
{
topTitle: `历史销售单品报表`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel
</Button>
]
}}
tableExtraRender={() => {
if (reqDetailList && reqDetailList.length > 0) {
let Total_CountSum: number = 0
let Total_OffAmountSum: number = 0
let Total_SellAmountSum: number = 0
reqDetailList.forEach((item: any) => {
Total_CountSum += item.Total_Count
Total_OffAmountSum += item.Total_OffAmount
Total_SellAmountSum += item.Total_SellAmount
})
return <div style={{ paddingLeft: 30, paddingBottom: 0 }}>
<ProDescriptions column={4} className="commity-sale-description" labelStyle={{ color: "#00000073" }}>
<ProDescriptions.Item label="销售数量" contentStyle={{ color: 'orangered' }}>{Total_CountSum ? numeral(Total_CountSum).format('0,0') : '0'}</ProDescriptions.Item>
<ProDescriptions.Item label="优惠金额" valueType="money" contentStyle={{ color: 'orangered' }}>{Total_OffAmountSum || '0.00'}</ProDescriptions.Item>
<ProDescriptions.Item label="对客营收" valueType="money" contentStyle={{ color: 'orangered' }}>{Total_SellAmountSum}</ProDescriptions.Item>
{/* <ProDescriptions.Item label="毛利率" contentStyle={{ color: 'orangered' }}></ProDescriptions.Item> */}
</ProDescriptions>
<ProDescriptions column={4} className="commity-sale-description" labelStyle={{ color: "#00000073" }}>
<ProDescriptions.Item label="制单人员">{currentUser.Name}</ProDescriptions.Item>
<ProDescriptions.Item label="制单时间">{moment().format('YYYY-MM-DD HH:mm:ss')}</ProDescriptions.Item>
<ProDescriptions.Item label="统计时间">{`${moment(searchParams?.StartTime).format('YYYY-MM')}${moment(searchParams?.EndTime).format('YYYY-MM')}`}</ProDescriptions.Item>
</ProDescriptions>
</div>
}
}}
/>
</div>
</div>
</div>
)
}
export default connect(({ user }: ConnectState) => ({
currentUser: user.currentUser
}))(saleHisReport);