519 lines
19 KiB
TypeScript
519 lines
19 KiB
TypeScript
// 收银员统计表
|
||
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 { getServerpartTree, 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 { handleGetPERSONSELLList } from "@/pages/reports/BusinessAnalysis/personSellReport/service";
|
||
import moment from "moment";
|
||
import ProDescriptions from "@ant-design/pro-descriptions";
|
||
import numeral from "numeral";
|
||
import { contractType } from "@/pages/contract/emun";
|
||
import { handleGetShopShortNamesGet } from "@/pages/reports/BusinessAnalysis/transactionAnalysis/service";
|
||
import PageTitleBox from "@/components/PageTitleBox";
|
||
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
|
||
import { formatTreeData } from "@/utils/format";
|
||
|
||
|
||
const personSellReport: 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 { 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 [shopNamesList, setShopNamesList] = useState<any>()
|
||
// 表格数据
|
||
const [tableData, setTableData] = useState<any>()
|
||
// 自营业态的选择的列表方法
|
||
const handleGetShopNamesList = async (id: any) => {
|
||
const req: any = {
|
||
ProvinceCode: currentUser?.USER_PROVINCE,
|
||
BusinessType: 1000,
|
||
ServerpartId: id || selectedId
|
||
}
|
||
const data = await handleGetShopShortNamesGet(req)
|
||
console.log('data', data)
|
||
if (data && data.length > 0) {
|
||
const obj: any = {}
|
||
if (data && data.length > 0) {
|
||
data.forEach((item: any) => {
|
||
obj[item] = item
|
||
})
|
||
}
|
||
setShopNamesList(obj)
|
||
}
|
||
}
|
||
|
||
|
||
const columns: any = [
|
||
{
|
||
title: '统计时间',
|
||
dataIndex: 'search_date',
|
||
valueType: 'dateTimeRange',
|
||
hideInTable: true,
|
||
hideInDescriptions: true,
|
||
colSize: 1,
|
||
initialValue: [moment(`${moment().add(-1, 'day').format('YYYY-MM-DD')} 12:00:00`).format('YYYY-MM-DD hh:mm:ss'), moment(`${moment().format('YYYY-MM-DD')} 12:00:00`).format('YYYY-MM-DD hh:mm:ss')],
|
||
search: {
|
||
transform: (value) => {
|
||
return {
|
||
SELL_ENDDATE_Start: value[0],
|
||
SELL_ENDDATE_End: value[1],
|
||
};
|
||
},
|
||
},
|
||
fieldProps: {
|
||
disabledDate: (current: any) => current && current < moment()
|
||
}
|
||
},
|
||
{
|
||
title: '经营模式',
|
||
key: 'BusinessType',
|
||
dataIndex: 'BusinessType',
|
||
valueType: 'select',
|
||
valueEnum: contractType,
|
||
hideInTable: true,
|
||
},
|
||
{
|
||
title: '自营业态',
|
||
dataIndex: 'shopNames',
|
||
hideInTable: true,
|
||
valueType: 'select',
|
||
valueEnum: shopNamesList,
|
||
// request: async () => {
|
||
// return await getFieldEnum({ FieldExplainField: 'BUSINESSTYPE' })
|
||
// },
|
||
fieldProps: {
|
||
multiple: true,
|
||
showSearch: true,
|
||
filterOption: (input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase()),
|
||
}
|
||
},
|
||
{
|
||
title: '序号',
|
||
dataIndex: 'index',
|
||
align: 'center',
|
||
width: 90,
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '服务区名称',
|
||
dataIndex: 'SERVERPART_NAME',
|
||
align: 'center',
|
||
width: 150,
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>门店名称</div>,
|
||
dataIndex: 'SHOPNAME',
|
||
align: 'left',
|
||
width: 200,
|
||
ellipsis: true,
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '交班时间',
|
||
dataIndex: 'SELL_ENDDATE',
|
||
width: 150,
|
||
align: 'center',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>客单数量</div>,
|
||
dataIndex: 'TICKETCOUNT',
|
||
width: 120,
|
||
align: 'right',
|
||
valueType: 'digit',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>销售数量</div>,
|
||
dataIndex: 'TOTALCOUNT',
|
||
width: 120,
|
||
align: 'right',
|
||
valueType: 'digit',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>优惠金额</div>,
|
||
dataIndex: 'TOTALOFFAMOUNT',
|
||
width: 120,
|
||
valueType: 'digit',
|
||
align: 'right',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>实收金额</div>,
|
||
dataIndex: 'RealActualCashpay',
|
||
width: 120,
|
||
valueType: 'digit',
|
||
align: 'right',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>对客营收</div>,
|
||
dataIndex: 'TOTALSELLAMOUNT',
|
||
width: 120,
|
||
valueType: 'digit',
|
||
align: 'right',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>缴款金额</div>,
|
||
dataIndex: 'CASHPAY',
|
||
width: 120,
|
||
valueType: 'digit',
|
||
align: 'right',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>现金支付</div>,
|
||
dataIndex: 'CASH',
|
||
width: 120,
|
||
valueType: 'digit',
|
||
align: 'right',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>长短款</div>,
|
||
dataIndex: 'DIFFERENT_PRICE',
|
||
width: 120,
|
||
valueType: 'digit',
|
||
align: 'right',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: <div style={{ textAlign: 'center' }}>移动支付</div>,
|
||
dataIndex: 'MobilyPay',
|
||
width: 120,
|
||
valueType: 'digit',
|
||
align: 'right',
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '机器号码',
|
||
dataIndex: 'MACHINECODE',
|
||
align: 'center',
|
||
width: 120,
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '收银员工号',
|
||
dataIndex: 'CASHWORKER_CODE',
|
||
align: 'center',
|
||
width: 140,
|
||
hideInSearch: true,
|
||
},
|
||
{
|
||
title: '收银员名称',
|
||
dataIndex: 'CASHIER_NAME',
|
||
width: 140,
|
||
align: 'center',
|
||
hideInSearch: true,
|
||
},
|
||
]
|
||
|
||
const exportTable = (e) => {
|
||
e.stopPropagation(); // 防止Collapse组件收起
|
||
const main = document.getElementsByClassName('saleReportHideBox')[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-personSellReport'); // 给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={'saleReportHideBox'} 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() || '')
|
||
handleGetShopNamesList(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
|
||
scroll={{ x: '100%', y: 'calc(100vh - 560px)' }}
|
||
// pagination={false}
|
||
headerTitle={<PageTitleBox props={props} />}
|
||
search={{ span: 8, defaultCollapsed: false }}
|
||
request={async (params) => {
|
||
if (!selectedId) {
|
||
return
|
||
}
|
||
const req = {
|
||
SearchParameter: {
|
||
...params,
|
||
SHOPCODES: selectedId
|
||
},
|
||
PageIndex: 1,
|
||
PageSize: 999999,
|
||
SortStr: 'SELL_ENDDATE desc'
|
||
}
|
||
setSearchParams(params)
|
||
console.log('req', req)
|
||
const data = await handleGetPERSONSELLList(req)
|
||
if (data.List && data.List.length > 0) {
|
||
const list: any = []
|
||
data.List.forEach((item: any, index: number) => {
|
||
item.index = index + 1
|
||
list.push(item)
|
||
})
|
||
list.unshift({
|
||
...data.OtherData,
|
||
SERVERPART_NAME: '合计'
|
||
})
|
||
console.log('list', list)
|
||
|
||
let fieldData: any = [
|
||
"TICKETCOUNT",
|
||
"TOTALCOUNT",
|
||
"TOTALOFFAMOUNT",
|
||
"RealActualCashpay",
|
||
"TOTALSELLAMOUNT",
|
||
"CASHPAY",
|
||
"CASH",
|
||
"DIFFERENT_PRICE",
|
||
"MobilyPay",
|
||
]
|
||
let enumList: any = []
|
||
let newPrintData: any = formatTreeData(JSON.parse(JSON.stringify(list)), fieldData, enumList, [], [])
|
||
|
||
setReqDetailList(newPrintData)
|
||
setTableData(list)
|
||
return { data: list, success: true }
|
||
}
|
||
setReqDetailList([])
|
||
setTableData([])
|
||
return { data: [], success: true }
|
||
}}
|
||
toolbar={{
|
||
actions: [
|
||
// <span style={{ visibility: 'hidden' }}>
|
||
// <ReactHTMLTableToExcel
|
||
// buttonText={'导出excel'}
|
||
// ref={downloadBtnRef}
|
||
// table="table-to-xls-personSellReport"
|
||
// filename={`服务区收银员报表${searchParams?.SELL_ENDDATE_Start}-${searchParams?.SELL_ENDDATE_End}`}
|
||
// sheet="sheet1"
|
||
// />
|
||
// </span>,
|
||
<Button
|
||
key="new"
|
||
type="primary"
|
||
onClick={(e) => {
|
||
if (reqDetailList && reqDetailList.length > 0) {
|
||
// 尝试一下 导出新方法
|
||
exportXlsxFromProColumnsExcelJS(columns,
|
||
reqDetailList,
|
||
`服务区收银员报表${searchParams?.SELL_ENDDATE_Start}-${searchParams?.SELL_ENDDATE_End}`,
|
||
// {
|
||
// 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 (tableData && tableData.length > 0) {
|
||
let TICKETCOUNTSum: number = 0
|
||
let TOTALCOUNTSum: number = 0
|
||
let TOTALOFFAMOUNTSum: number = 0
|
||
let RealActualCashpaySum: number = 0
|
||
let TOTALSELLAMOUNTSum: number = 0
|
||
let CASHPAYSum: number = 0
|
||
let CASHSum: number = 0
|
||
let DIFFERENT_PRICESum: number = 0
|
||
let MobilyPaySum: number = 0
|
||
|
||
tableData.forEach((item: any) => {
|
||
TICKETCOUNTSum += item.TICKETCOUNT
|
||
TOTALCOUNTSum += item.TOTALCOUNT
|
||
TOTALOFFAMOUNTSum += item.TOTALOFFAMOUNT
|
||
RealActualCashpaySum += item.RealActualCashpay
|
||
TOTALSELLAMOUNTSum += item.TOTALSELLAMOUNT
|
||
CASHPAYSum += item.CASHPAY
|
||
CASHSum += item.CASH
|
||
DIFFERENT_PRICESum += item.DIFFERENT_PRICE
|
||
MobilyPaySum += item.MobilyPay
|
||
})
|
||
return <div style={{ paddingLeft: 30, paddingBottom: 0 }}>
|
||
<ProDescriptions column={6} className="commity-sale-description" labelStyle={{ color: "#00000073" }}>
|
||
<ProDescriptions.Item span={1} label="客单数量" contentStyle={{ color: 'orangered' }}>{TICKETCOUNTSum ? numeral(TICKETCOUNTSum).format('0,0') : '0'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={1} label="销售数量" contentStyle={{ color: 'orangered' }}>{TOTALCOUNTSum ? numeral(TOTALCOUNTSum).format('0,0') : '0'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={1} label="优惠金额" valueType="money" contentStyle={{ color: 'orangered' }}>{TOTALOFFAMOUNTSum || '0.00'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={1} label="实收金额" valueType="money" contentStyle={{ color: 'orangered' }}>{RealActualCashpaySum || '0.00'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={2} label="对客营收" valueType="money" contentStyle={{ color: 'orangered' }}>{TOTALSELLAMOUNTSum || '0.00'}</ProDescriptions.Item>
|
||
</ProDescriptions>
|
||
<ProDescriptions column={6} className="commity-sale-description" labelStyle={{ color: "#00000073" }}>
|
||
<ProDescriptions.Item span={1} label="缴款金额" valueType="money" contentStyle={{ color: 'orangered' }}>{CASHPAYSum || '0.00'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={1} label="现金支付" valueType="money" contentStyle={{ color: 'orangered' }}>{CASHSum || '0.00'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={1} label="长短款额:" valueType="money" contentStyle={{ color: 'orangered' }}>{DIFFERENT_PRICESum || '0.00'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={1} label="移动支付" valueType="money" contentStyle={{ color: 'orangered' }}>{MobilyPaySum || '0.00'}</ProDescriptions.Item>
|
||
<ProDescriptions.Item span={2} label="统计时间">{`${moment(searchParams?.SELL_ENDDATE_Start).format('YYYY-MM-DD HH时')}至${moment(searchParams?.SELL_ENDDATE_End).format('YYYY-MM-DD HH时')}`}</ProDescriptions.Item>
|
||
</ProDescriptions>
|
||
</div>
|
||
}
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default connect(({ user }: ConnectState) => ({
|
||
currentUser: user.currentUser
|
||
}))(personSellReport);
|