This commit is contained in:
ylj20011123 2025-09-02 18:28:02 +08:00
parent d3a4c2ad28
commit 30fa7f3f4c
15 changed files with 879 additions and 181 deletions

View File

@ -1,6 +1,6 @@
{
"name": "ant-design-pro",
"version": "4.5.42",
"version": "4.5.45",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
@ -68,6 +68,7 @@
"compression-webpack-plugin": "^11.1.0",
"crypto-js": "^4.1.1",
"eslint-webpack-plugin": "^4.2.0",
"exceljs": "^4.4.0",
"html-webpack-plugin": "^5.6.0",
"increase-memory-limit": "^1.0.7",
"insert-css": "^2.0.0",

View File

@ -19,6 +19,7 @@ import session from "@/utils/session";
import { handleGetCHECKCOMMODITYList, handleGetTypeStatisticsList } from "../service";
import Draggable from "react-draggable";
import InventoryDetails from "../InventoryDetails";
import { exportXlsxFromProColumns, exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const InventoryCategory: React.FC<{ currentUser: CurrentUser }> = (props) => {
@ -855,7 +856,7 @@ const InventoryCategory: React.FC<{ currentUser: CurrentUser }> = (props) => {
setReqDetailList([])
return { data: [], success: true }
}
const req: any = {
ProvinceCode: currentUser?.ProvinceCode,
ServerpartId: params.servicePartId ? params.servicePartId : '',
@ -868,7 +869,7 @@ const InventoryCategory: React.FC<{ currentUser: CurrentUser }> = (props) => {
const data = await handleGetTypeStatisticsList(req)
console.log('data321312', data);
setReqDetailList(data)
if (data && data.length > 0) {
return { data, success: true }
}
@ -881,7 +882,7 @@ const InventoryCategory: React.FC<{ currentUser: CurrentUser }> = (props) => {
buttonText={'导出excel'}
ref={downloadBtnRef}
table="table-to-xls-InventoryCategory"
filename={`进销存类别报表${searchParams?.StartDate}-${searchParams?.EndDate}`}
filename={`进销存类别报表${searchParams?.InventoryTime ? searchParams?.InventoryTime.split('-')[1] : ""}`}
sheet="sheet1"
/>
</span>,
@ -890,16 +891,30 @@ const InventoryCategory: React.FC<{ currentUser: CurrentUser }> = (props) => {
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(currentDataType === '1' ? [...columns, ...columnsType1] : currentDataType === '2' ? [...columns, ...columnsType2] : [],
reqDetailList,
`进销存类别报表${searchParams?.InventoryTime ? searchParams?.InventoryTime.split('-')[1] : ""}`,
{
topTitle: `进销存类别报表`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel

View File

@ -621,7 +621,7 @@ const COMMODITYINFO = ({ onShow, onCancel, parentRow }: DetailProps) => {
SERVERPART_ID: parentRow?.SERVERPART_ID || "",
SERVERPARTSHOP_ID: parentRow?.SERVERPARTSHOP_ID || "",
// CHECKDATE: params.InventoryTime ? params.InventoryTime : "",
COMMODITY_CODE: parentRow?.COMMODITY_CODE || "",
COMMODITY_IDS: parentRow?.COMMODITY_ID || "",
COMMODITY_BARCODE: parentRow?.COMMODITY_BARCODE || ""
},
PageIndex: 1,

View File

@ -20,6 +20,7 @@ import { getMyShopList } from "@/pages/account/center/sevice";
import { render } from "react-dom";
import { initial } from "lodash";
import InventoryDetailModal from "./components/InventoryDetailModal";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const InventoryDetails: React.FC<{ currentUser: CurrentUser, isComponents?: any, parentRow?: any, birthIn?: any, parentSearchForm?: any }> = (props) => {
@ -1057,16 +1058,30 @@ const InventoryDetails: React.FC<{ currentUser: CurrentUser, isComponents?: any,
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(isComponents ? [...columns, ...columnsType1] : currentDataType === '1' ? [...columns, ...columnsType1] : currentDataType === '2' ? [...columns, ...columnsType2] : [],
reqDetailList,
`进销存明细报表${searchParams?.InventoryTime || ""}`,
{
topTitle: `${currentShopInfo?.SERVERPART_NAME}${currentShopInfo?.SHOPNAME}进销存报表`, // 顶部大标题
infoRowLeft: `统计方式:按成本结算(${currentDataType === '1' ? '含税' : '除税'})`,//左侧文字
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel

View File

@ -7,6 +7,7 @@ import Draggable from "react-draggable";
import ProTable, { ActionType } from "@ant-design/pro-table";
import { handleGetShopInventoryList } from "../../service";
import InventoryDetailModal from "../../InventoryDetails/components/InventoryDetailModal";
import { highlightText } from "@/utils/highlightText";
type DetailProps = {
onShow: any
@ -15,8 +16,9 @@ type DetailProps = {
currentUser: any
ServerpartId: any // 服务区id
shopId: any // 门店id
searchParams?: any
}
const inventoryDetail = ({ onShow, onCancel, parentRow, currentUser, ServerpartId, shopId }: DetailProps) => {
const inventoryDetail = ({ onShow, onCancel, parentRow, currentUser, ServerpartId, shopId, searchParams }: DetailProps) => {
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const draggleRef = React.createRef<any>()
@ -84,7 +86,7 @@ const inventoryDetail = ({ onShow, onCancel, parentRow, currentUser, ServerpartI
setCurrentRow(record)
setShowDetail(true)
}}>
{record?.COMMODITY_NAME}
{highlightText(record?.COMMODITY_NAME, searchParams?.searchText)}
</a> : ""
}
},
@ -95,6 +97,21 @@ const inventoryDetail = ({ onShow, onCancel, parentRow, currentUser, ServerpartI
width: 120,
ellipsis: true,
hideInSearch: true,
render: (_, record) => {
return record?.COMMODITY_BARCODE ? highlightText(record?.COMMODITY_BARCODE, searchParams?.searchText) : "-"
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "COMMODITY_BRAND",
align: 'left',
width: 120,
ellipsis: true,
hideInSearch: true,
render: (_, record) => {
return record?.COMMODITY_BRAND ? highlightText(record?.COMMODITY_BRAND, searchParams?.searchText) : "-"
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,

View File

@ -18,6 +18,8 @@ import { handleGetNestingCOMMODITYTYPETree } from "@/pages/reports/productContro
import { handleGetInventoryList } from "../service";
import { keyBy } from "lodash";
import InventoryDetail from "./components/inventoryDetail";
import { highlightText } from "@/utils/highlightText";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const { Text } = Typography;
@ -27,6 +29,7 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const [reqDetailList, setReqDetailList] = useState<any>(); // 合计项数据源
const [reqDefaultDetailList, setReqDefaultDetailList] = useState<any>(); // 合计项数据源
const [printOut, setPrintOut] = useState<any>(); // 打印数据的内容
const [collapsible, setCollapsible] = useState<boolean>(false)
const [treeView, setTreeView] = useState<any>()
@ -53,6 +56,10 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
const [showDetail, setShowDetail] = useState<boolean>(false)
// 当前选中的服务区id
const [currentSelectServerpartId, setCurrentSelectServerpartId] = useState<any>()
// 判断是不是点击了 排序、分页等
const isNoTableReload = useRef<boolean>(false)
// 表格数据
const [tableData, setTableData] = useState<any>()
const buildLeafMaps = (nodes: any[]) => {
@ -114,7 +121,6 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
}
},
{
title: "序号",
dataIndex: "index",
@ -136,7 +142,7 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
setCurrentRow(record)
setShowDetail(true)
}}>
{record?.COMMODITY_NAME}
{highlightText(record?.COMMODITY_NAME, searchParams?.searchText)}
</a> : "-"
}
},
@ -147,15 +153,21 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
width: 120,
ellipsis: true,
hideInSearch: true,
render: (_, record) => {
return record?.COMMODITY_BARCODE ? highlightText(record?.COMMODITY_BARCODE, searchParams?.searchText) : "-"
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "COMMODITY_BRAND",
align: 'left',
width: 120,
ellipsis: true,
hideInSearch: true,
render: (_, record) => {
return record?.COMMODITY_BRAND ? highlightText(record?.COMMODITY_BRAND, searchParams?.searchText) : "-"
}
},
// {
// title: <div style={{ textAlign: 'center' }}>商品编码</div>,
// dataIndex: "COMMODITY_BRAND",
// align: 'left',
// width: 120,
// ellipsis: true,
// hideInSearch: true,
// },
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "COMMODITY_RULE",
@ -169,8 +181,9 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
dataIndex: "OVERPLUSCOUNT",
align: 'right',
valueType: 'digit',
sorter: true,
defaultSortOrder: "descend",
// 这里如果要加默认的排序的话 需要单独在导出表格里面 单领出来
sorter: (a, b) => a.OVERPLUSCOUNT - b.OVERPLUSCOUNT,
// defaultSortOrder: "descend",
width: 120,
ellipsis: true,
hideInSearch: true,
@ -180,7 +193,7 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
dataIndex: "OVERPLUS_AMOUNT",
align: 'right',
valueType: 'digit',
sorter: true,
sorter: (a, b) => a.OVERPLUS_AMOUNT - b.OVERPLUS_AMOUNT,
width: 120,
ellipsis: true,
hideInSearch: true,
@ -190,7 +203,7 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
dataIndex: "OVERPLUS_PRICE",
align: 'right',
valueType: 'digit',
sorter: true,
sorter: (a, b) => a.OVERPLUS_PRICE - b.OVERPLUS_PRICE,
width: 120,
ellipsis: true,
hideInSearch: true,
@ -199,7 +212,7 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "OVERPLUS_XS_AMOUNT",
align: 'right',
sorter: true,
sorter: (a, b) => a.OVERPLUS_XS_AMOUNT - b.OVERPLUS_XS_AMOUNT,
valueType: 'digit',
width: 120,
ellipsis: true,
@ -210,7 +223,7 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
dataIndex: "OVERPLUS_XS_PRICE",
align: 'right',
valueType: 'digit',
sorter: true,
sorter: (a, b) => a.OVERPLUS_XS_PRICE - b.OVERPLUS_XS_PRICE,
width: 120,
ellipsis: true,
hideInSearch: true,
@ -244,6 +257,50 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
setShowDetail(false)
}
const sortListByState = (
list: any[],
state?: { field?: string; order?: 'ascend' | 'descend' }
) => {
const arr = Array.isArray(list) ? list.slice() : [];
if (!state?.field || !state?.order) return arr; // ★ 返回副本,避免共享引用
const { field, order } = state;
const factor = order === 'ascend' ? 1 : -1;
const getVal = (v: any) => {
if (v == null) return { type: 3, v: Number.POSITIVE_INFINITY }; // 统一丢到最后
if (typeof v === 'number') return { type: 0, v };
if (v instanceof Date) return { type: 1, v: v.getTime() };
if (typeof v === 'string') {
const t = Date.parse(v);
if (!Number.isNaN(t)) return { type: 1, v: t };
const n = Number(v);
// 纯数字字符串才按数字比较(去掉前后空格,避免把 '0012' 和 '12 '误判为不同)
if (!Number.isNaN(n) && v.trim() !== '' && String(n) === v.trim())
return { type: 0, v: n };
return { type: 2, v: v }; // 普通字符串
}
// 其它类型转字符串
return { type: 2, v: String(v) };
};
return arr
.map((item, idx) => {
const val = getVal(item?.[field]);
return { item, idx, val };
})
.sort((a, b) => {
// 先按类型分组,再按值比
if (a.val.type !== b.val.type) return (a.val.type - b.val.type) * factor;
if (a.val.v < b.val.v) return -1 * factor;
if (a.val.v > b.val.v) return 1 * factor;
// 值相等,用原始下标保证稳定
return a.idx - b.idx;
})
.map(d => d.item);
};
return (
<div ref={(el) => {
@ -353,12 +410,10 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
search={{ span: 6 }}
request={async (params, sorter) => {
console.log('paramsparamsparamsparamsparamsparams', params);
if (!selectedId) {
if (!selectedId || isNoTableReload.current) {
isNoTableReload.current = false
return
}
console.log('selectedIdselectedIdselectedId', selectedId);
console.log('handleAllShopIdhandleAllShopIdhandleAllShopIdhandleAllShopId', handleAllShopId);
// 根据门店去判断 是哪个服务区里面的 再把服务区的id 记录下来 而且要去重
let serverpartId: any = []
@ -367,8 +422,6 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
if (list && list.length > 0) {
list.forEach((item: any) => {
for (let key in handleAllShopId) {
console.log('handleAllShopId[key]', handleAllShopId[key]);
if (handleAllShopId[key] && handleAllShopId[key].length > 0) {
if (handleAllShopId[key].indexOf(Number(item)) !== -1) {
if (serverpartId && serverpartId.length > 0) {
@ -385,8 +438,6 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
}
}
console.log('serverpartIdserverpartIdserverpartIdserverpartId', serverpartId);
const sortstr = Object.keys(sorter).map(n => {
const value = sorter[n]
return value ? `${n} ${value.replace('end', '')}` : ''
@ -397,7 +448,7 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
ServerpartId: serverpartId && serverpartId.length > 0 ? serverpartId.toString() : "",
ServerpartShopId: selectedId,
CommodityTypeId: params?.COMMODITY_TYPE ? params?.COMMODITY_TYPE.toString() : "",
SearchKeyName: "COMMODITY_NAME,COMMODITY_BARCODE,COMMODITY_BRAND",
SearchKeyName: "COMMODITY_NAME,COMMODITY_BRAND,COMMODITY_BARCODE",
SearchKeyValue: params?.searchText || "",
PageIndex: "1",
PageSize: "999999",
@ -410,11 +461,14 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
console.log('datadata', data);
setTableSumObj(data.OtherData)
setReqDetailList(data.List)
if (data.List && data.List.length > 0) {
return { data: data.List, success: true }
}
return { data: [], success: true }
setReqDefaultDetailList(data.List)
setTableData(data.List)
// if (data.List && data.List.length > 0) {
// return { data: data.List, success: true }
// }
// return { data: [], success: true }
}}
dataSource={tableData}
toolbar={{
actions: [
<span style={{ visibility: 'hidden' }}>
@ -431,16 +485,30 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(columns,
reqDetailList,
`库存信息统计表`,
{
topTitle: `库存信息统计表`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel
@ -520,13 +588,24 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) =>
</Table.Summary>
)
}}
onChange={(_pagination: any, _filters: any, sorter: any, extra: any) => {
console.log('onChange');
isNoTableReload.current = true
if (extra?.action === 'sort') {
const field = Array.isArray(sorter) ? sorter[0]?.field : sorter?.field;
const order = Array.isArray(sorter) ? sorter[0]?.order : sorter?.order;
const newList: any = sortListByState(reqDefaultDetailList, { field, order })
console.log('newListnewList', newList);
setReqDetailList(newList)
}
}}
/>
</div>
</div>
{/* 库存的详情信息 */}
<InventoryDetail onShow={showDetail} onCancel={handleClose} parentRow={currentRow} currentUser={currentUser} ServerpartId={currentSelectServerpartId} shopId={selectedId} />
<InventoryDetail onShow={showDetail} onCancel={handleClose} parentRow={currentRow} currentUser={currentUser} ServerpartId={currentSelectServerpartId} shopId={selectedId} searchParams={searchParams} />
</div>
)

View File

@ -19,6 +19,7 @@ import session from "@/utils/session";
import InventoryDetailModal from "../InventoryDetails/components/InventoryDetailModal";
import Draggable from "react-draggable";
import InventoryDetails from "../InventoryDetails";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const oneProductMultipleSizes: React.FC<{ currentUser: CurrentUser }> = (props) => {
@ -176,8 +177,8 @@ const oneProductMultipleSizes: React.FC<{ currentUser: CurrentUser }> = (props)
}
},
{
title: "商品码",
dataIndex: "COMMODITY_BARCODE",
title: "商品码",
dataIndex: "COMMODITY_CODE",
align: 'center',
width: 120,
ellipsis: true,
@ -935,16 +936,29 @@ const oneProductMultipleSizes: React.FC<{ currentUser: CurrentUser }> = (props)
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(currentDataType === '1' ? [...columns, ...columnsType1] : currentDataType === '2' ? [...columns, ...columnsType2] : [],
reqDetailList,
`一品多码统计表`,
{
topTitle: `一品多码统计表`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel

View File

@ -15,6 +15,7 @@ import LeftSelectTree from "@/pages/reports/settlementAccount/component/leftSele
import PageTitleBox from "@/components/PageTitleBox";
import moment from "moment";
import { handleGetMERCHANTSList, handleGetStorageBackSummary } from "../service";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
@ -23,6 +24,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const [reqDetailList, setReqDetailList] = useState<any>(); // 合计项数据源
const [reqDefaultDetailList, setReqDefaultDetailList] = useState<any>(); // 合计项数据源
const [printOut, setPrintOut] = useState<any>(); // 打印数据的内容
const [collapsible, setCollapsible] = useState<boolean>(false)
const [treeView, setTreeView] = useState<any>()
@ -39,6 +41,10 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
const [searchParams, setSearchParams] = useState<any>()
// 拿到服务区下的所有门店id集合
const [handleAllShopId, sethandleAllShopId] = useState<any>()
// 判断是不是点击了 排序、分页等
const isNoTableReload = useRef<boolean>(false)
// 表格数据
const [tableData, setTableData] = useState<any>()
const columns: any = [
{
@ -71,8 +77,13 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
valueType: 'select',
request: async () => {
const req: any = {
PROVINCE_CODE: currentUser?.ProvinceCode,
MERCHANTS_STATE: 1
SearchParameter: {
PROVINCE_CODE: currentUser?.ProvinceCode,
MERCHANTS_STATE: 1
},
PageIndex: 1,
PageSize: 999999
}
const data = await handleGetMERCHANTSList(req)
console.log('datadatadatadata', data);
@ -129,14 +140,16 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
ellipsis: true,
hideInSearch: true,
},
// defaultSortOrder: 'descend',
// 如果要默认排序的话 默认的那一项 要在导出里面单独去写
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "Storage_Count",
align: 'right',
valueType: 'digit',
width: 120,
sorter: true,
defaultSortOrder: 'descend',
sorter: (a, b) => a.Storage_Count - b.Storage_Count,
ellipsis: true,
hideInSearch: true,
},
@ -146,7 +159,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
align: 'right',
valueType: 'digit',
width: 120,
sorter: true,
sorter: (a, b) => a.Storage_TaxAmount - b.Storage_TaxAmount,
ellipsis: true,
hideInSearch: true,
},
@ -156,7 +169,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
align: 'right',
valueType: 'digit',
width: 120,
sorter: true,
sorter: (a, b) => a.Storage_Amount - b.Storage_Amount,
ellipsis: true,
hideInSearch: true,
},
@ -166,7 +179,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
align: 'right',
valueType: 'digit',
width: 120,
sorter: true,
sorter: (a, b) => a.Back_Count - b.Back_Count,
ellipsis: true,
hideInSearch: true,
},
@ -176,7 +189,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
align: 'right',
valueType: 'digit',
width: 120,
sorter: true,
sorter: (a, b) => a.Back_TaxAmount - b.Back_TaxAmount,
ellipsis: true,
hideInSearch: true,
},
@ -186,7 +199,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
align: 'right',
valueType: 'digit',
width: 120,
sorter: true,
sorter: (a, b) => a.Back_Amount - b.Back_Amount,
ellipsis: true,
hideInSearch: true,
}
@ -239,10 +252,152 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
width: 200,
ellipsis: true,
hideInSearch: true,
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "ServerpartShop_Name",
align: 'center',
width: 150,
ellipsis: true,
hideInSearch: true,
},
]
type SortState = { field?: string; order?: 'ascend' | 'descend' };
type KeyOf<T> = Extract<keyof T, string>;
interface SortTreeOptions<T = any> {
childrenKey?: KeyOf<T>; // 子节点字段名,默认 'children'
}
/** 树形排序:父与父排,父内的子与子排(递归) */
const sortListByState = <T extends Record<string, any>>(
list: T[] = [],
state?: SortState,
options?: SortTreeOptions<T>
): T[] => {
const { childrenKey = 'children' } = options || {};
// 拷贝数组,避免改原数据
const arr = Array.isArray(list) ? list.slice() : [];
if (!state?.field || !state?.order) {
// 即便无排序条件也返回副本,避免引用共享
return arr.map(cloneNode);
}
const { field, order } = state;
const factor = order === 'ascend' ? 1 : -1;
// 取可比较值:数值 > 时间 > 字符串 > 其它null/undefined 放最后
const getComparable = (v: any) => {
if (v == null) return { type: 9, v: Number.POSITIVE_INFINITY };
if (typeof v === 'number') return { type: 0, v };
if (v instanceof Date) return { type: 1, v: v.getTime() };
if (typeof v === 'string') {
const t = Date.parse(v);
if (!Number.isNaN(t)) return { type: 1, v: t };
const n = Number(v);
if (!Number.isNaN(n) && v.trim() !== '' && String(n) === v.trim()) {
return { type: 0, v: n };
}
return { type: 2, v };
}
return { type: 3, v: String(v) };
};
// 比较器:同层内比较 field值相等用原始下标保证稳定
const withIndex = arr.map((item, idx) => ({ item, idx }));
const compare = (a: any, b: any) => {
const va = getComparable(a?.[field as string]);
const vb = getComparable(b?.[field as string]);
if (va.type !== vb.type) return (va.type - vb.type) * factor;
if (va.v < vb.v) return -1 * factor;
if (va.v > vb.v) return 1 * factor;
return 0; // 真正稳定通过 map 阶段的 idx 来保证
};
// 顶层排序(父与父)
const topSorted = withIndex
.slice()
.sort((A, B) => {
const c = compare(A.item, B.item);
return c !== 0 ? c : A.idx - B.idx;
})
.map(({ item }) => item);
// 递归对子节点排序(每个父内子与子)
return topSorted.map((node) => {
const cloned = cloneNode(node);
const children = cloned?.[childrenKey] as T[] | undefined;
if (Array.isArray(children) && children.length) {
// 同样做稳定排序
const childrenWithIndex = children.map((ch, i) => ({ ch, i }));
const childSorted = childrenWithIndex
.slice()
.sort((A, B) => {
const c = compare(A.ch, B.ch);
return c !== 0 ? c : A.i - B.i;
})
.map(({ ch }) => ch);
// 递归:若还有下一级,继续排
cloned[childrenKey] = sortListByState(childSorted, state, { childrenKey });
}
return cloned;
});
function cloneNode<N extends Record<string, any>>(n: N): N {
if (!n || typeof n !== 'object') return n;
const c: any = { ...n };
// children 数组引用也要分离(排序时会重建,保险起见先浅拷贝)
if (Array.isArray(c[childrenKey])) c[childrenKey] = c[childrenKey].slice();
return c;
}
};
const isParent = (r: any) => !r?.Commodity_Id;
// 父级行合并单元格里要显示的自定义内容(你可按需改)
const renderParentCell = (record: any) => {
// 你可以自由拼接:服务区/供应商/门店等
return (
<div style={{ paddingLeft: 8, fontWeight: 600 }}>
{record?.Supplier_Name}-{record?.Serverpart_Name}-{record?.ServerpartShop_Name}
</div>
);
};
const EXPORT_COLSPAN = exportColumns.length;
const mergedExportColumns = [
{
...exportColumns[0],
// 第一列:父级→合并整段导出列;子级→显示序号(原 valueType:'index' 需手动展示)
render: (_: any, record: any, index: number) => {
if (isParent(record)) {
return {
children: renderParentCell(record),
props: { colSpan: EXPORT_COLSPAN },
};
}
// 子级:正常显示序号
return { children: index + 1, props: {} };
},
},
// 其余导出列父级隐藏colSpan:0子级正常显示
...exportColumns.slice(1).map((col: any) => ({
...col,
render: (text: any, record: any) => {
if (isParent(record)) {
return { children: null, props: { colSpan: 0 } };
}
return { children: text, props: {} };
},
})),
];
return (
<div ref={(el) => {
// 打印报表
@ -283,7 +438,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
{
showExportTable && reqDetailList && reqDetailList.length > 0 ?
<ProTable
columns={[...exportColumns, ...columns.slice(4, columns.length)]}
columns={[...mergedExportColumns, ...columns.slice(5, columns.length)]}
dataSource={reqDetailList}
pagination={false}
expandable={{
@ -316,10 +471,12 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
scroll={{ x: "100%", y: "calc(100vh - 410px)" }}
headerTitle={<PageTitleBox props={props} />} // 列表表头
search={{ span: 6 }}
dataSource={tableData}
request={async (params, sorter) => {
console.log('selectedIdselectedIdselectedId', selectedId);
if (!selectedId) {
if (!selectedId || isNoTableReload.current) {
isNoTableReload.current = false
return
}
@ -331,8 +488,6 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
if (list && list.length > 0) {
list.forEach((item: any) => {
for (let key in handleAllShopId) {
console.log('handleAllShopId[key]', handleAllShopId[key]);
if (handleAllShopId[key] && handleAllShopId[key].length > 0) {
if (handleAllShopId[key].indexOf(Number(item)) !== -1) {
if (serverpartId && serverpartId.length > 0) {
@ -349,10 +504,10 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
}
}
// 排序字段
const sortstr = Object.keys(sorter).map(n => {
const value = sorter[n]
return value ? `${n} ${value.replace('end', '')}` : ''
})
// const sortstr = Object.keys(sorter).map(n => {
// const value = sorter[n]
// return value ? `${n} ${value.replace('end', '')}` : ''
// })
const req: any = {
ServerpartId: serverpartId && serverpartId.length > 0 ? serverpartId.toString() : "",
ServerpartShopId: selectedId,
@ -361,31 +516,38 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
EndDate: params?.EndDate || "",
SearchKeyName: "",
SearchKeyValue: "",
sortstr: sortstr.length ? sortstr.toString() : "",
// sortstr: sortstr.length ? sortstr.toString() : "",
}
setSearchParams(params)
const data = await handleGetStorageBackSummary(req)
console.log('dafjkdjaf', data);
if (data && data.length > 0) {
let exportList: any = []
data.forEach((item: any) => {
if (item.children && item.children.length > 0) {
item.children.forEach((subItem: any) => {
let newChildren: any = []
if (subItem.children && subItem.children.length > 0) {
subItem.children.forEach((thirdItem: any) => {
exportList.push(thirdItem)
// exportList.push(thirdItem)
newChildren.push(thirdItem)
})
}
subItem.children = newChildren
exportList.push(subItem)
})
}
})
setReqDetailList(exportList)
return { data, success: true }
setReqDefaultDetailList(exportList)
setTableData(data)
// return { data, success: true }
} else {
setTableData([])
// return { data: [], success: true }
}
return { data: [], success: true }
}}
toolbar={{
actions: [
@ -402,23 +564,47 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => {
key="new"
type="primary"
onClick={(e) => {
console.log('reqDetailListreqDetailListreqDetailList', reqDetailList);
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(columns,
reqDetailList,
`入库退货统计表${searchParams?.StartDate}-${searchParams?.EndDate}`,
{
topTitle: `入库退货统计表`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel
</Button>
]
}}
onChange={(_pagination: any, _filters: any, sorter: any, extra: any) => {
console.log('onChange');
isNoTableReload.current = true
if (extra?.action === 'sort') {
const field = Array.isArray(sorter) ? sorter[0]?.field : sorter?.field;
const order = Array.isArray(sorter) ? sorter[0]?.order : sorter?.order;
const newList: any = sortListByState(reqDefaultDetailList, { field, order }, { childrenKey: 'children' })
console.log('newListnewList', newList);
setReqDetailList(newList)
}
}}
/>
</div>
</div>

View File

@ -7,13 +7,15 @@ import Draggable from "react-draggable";
import ProTable, { ActionType } from "@ant-design/pro-table";
import { handleGetBACKCOMMODITYDetail, handleGetBACKCOMMODITYList, handleGetSALESTOREPROINSTDetail } from "../../service";
import { handleNewPrint, handleNewPrintAHJG } from "@/utils/format";
import { highlightText } from "@/utils/highlightText";
type DetailProps = {
showDetail: boolean;
parentRow: any;
onCencel: any;
searchParams?: any
}
const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => {
const ReturnGoodsTable = ({ showDetail, parentRow, onCencel, searchParams }: DetailProps) => {
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const draggleRef = React.createRef<any>()
@ -50,32 +52,38 @@ const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => {
width: 120,
dataIndex: "COMMODITY_BARCODE",
align: 'center',
ellipsis: true
ellipsis: true,
render: (_, record) => {
return highlightText(record?.COMMODITY_BARCODE, searchParams?.searchText)
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
width: 150,
width: 250,
dataIndex: "COMMODITY_NAME",
align: 'left',
ellipsis: true
ellipsis: true,
render: (_, record) => {
return highlightText(record?.COMMODITY_NAME, searchParams?.searchText)
}
},
{
title: '退货门店',
width: 120,
width: 150,
dataIndex: "SHOPNAME",
align: 'center',
ellipsis: true
},
{
title: <div style={{ textAlign: 'center' }}></div>,
width: 150,
width: 250,
dataIndex: "SUPPLIER_NAME",
align: 'left',
ellipsis: true
},
{
title: <div style={{ textAlign: 'center' }}>退</div>,
width: 120,
width: 100,
dataIndex: "BACK_COUNT",
valueType: 'digit',
align: 'right',
@ -83,14 +91,14 @@ const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => {
},
{
title: <div style={{ textAlign: 'center' }}></div>,
width: 120,
width: 100,
dataIndex: "DUTY_PARAGRAPH",
align: 'right',
ellipsis: true
},
{
title: <div style={{ textAlign: 'center' }}></div>,
width: 120,
width: 100,
dataIndex: "PURCHASE_TAXPRICE",
valueType: 'digit',
align: 'right',
@ -98,7 +106,7 @@ const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => {
},
{
title: <div style={{ textAlign: 'center' }}></div>,
width: 120,
width: 100,
dataIndex: "BACKTAXPRICE",
valueType: 'digit',
align: 'right',
@ -112,10 +120,10 @@ const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => {
ellipsis: true
},
{
title: '退货原由',
width: 120,
title: <div style={{ textAlign: 'center' }}>退</div>,
width: 250,
dataIndex: "BACK_DESC",
align: 'center',
align: 'left',
ellipsis: true
}
]
@ -226,6 +234,7 @@ const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => {
pagination={false}
columns={columns}
options={false}
scroll={{ x: "100%", y: 400 }}
request={async () => {
console.log('parentRow', parentRow);
if (!parentRow?.SALESTOREPROINST_ID) {

View File

@ -16,6 +16,7 @@ import PageTitleBox from "@/components/PageTitleBox";
import moment from "moment";
import { handleGetSALESTOREPROINSTList } from "../service";
import ReturnGoodsTable from "./components/returnGoodsTable";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
@ -76,19 +77,19 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
},
initialValue: [moment().startOf('M'), moment()],
},
{
title: '退货模式',
dataIndex: "",
align: 'center',
valueType: 'select',
width: 120,
ellipsis: true,
hideInTable: true,
fieldProps: {
options: [{ label: "全部", value: '' }, { label: "自采", value: 5 }, { label: "统配", value: 107 }]
},
initialValue: ''
},
// {
// title: '退货模式',
// dataIndex: "",
// align: 'center',
// valueType: 'select',
// width: 120,
// ellipsis: true,
// hideInTable: true,
// fieldProps: {
// options: [{ label: "全部", value: '' }, { label: "自采", value: 5 }, { label: "统配", value: 107 }]
// },
// initialValue: ''
// },
{
title: "序号",
@ -125,11 +126,12 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
// },
{
title: <div style={{ textAlign: 'center' }}>退</div>,
dataIndex: "退货数量",
dataIndex: "OPERATE_COUNT",
align: 'right',
width: 150,
ellipsis: true,
sorter: true,
// sorter: true,
sorter: (a, b) => a.OPERATE_COUNT - b.OPERATE_COUNT,
hideInSearch: true,
render: (_, record) => {
return <a onClick={() => {
@ -138,40 +140,50 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
setCurrentRow(record)
setOnShow(true)
}}>
1
{record?.OPERATE_COUNT || ""}
</a>
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "合计含税进价",
dataIndex: "OPERATE_TAXAMOUNT",
align: 'right',
width: 150,
sorter: true,
sorter: (a, b) => a.OPERATE_TAXAMOUNT - b.OPERATE_TAXAMOUNT,
ellipsis: true,
hideInSearch: true,
},
{
title: "待审核人",
dataIndex: "待审核人",
align: 'center',
width: 150,
ellipsis: true,
hideInSearch: true,
},
{
title: "流程名称",
dataIndex: "流程名称",
align: 'center',
title: <div style={{ textAlign: 'center' }}></div>,
dataIndex: "OPERATE_AMOUNT",
align: 'right',
width: 150,
sorter: (a, b) => a.OPERATE_AMOUNT - b.OPERATE_AMOUNT,
ellipsis: true,
hideInSearch: true,
},
// {
// title: "待审核人",
// dataIndex: "待审核人",
// align: 'center',
// width: 150,
// ellipsis: true,
// hideInSearch: true,
// },
// {
// title: "流程名称",
// dataIndex: "流程名称",
// align: 'center',
// width: 150,
// ellipsis: true,
// hideInSearch: true,
// },
{
title: "流程开始时间",
dataIndex: "CREATEDATE",
align: 'center',
width: 150,
sorter: (a, b) => new Date(a.CREATEDATE).getTime() - new Date(b.CREATEDATE).getTime(),
ellipsis: true,
hideInSearch: true,
},
@ -179,7 +191,9 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
title: "流程结束时间",
dataIndex: "ENDDATE",
align: 'center',
defaultSortOrder: 'descend',
width: 150,
sorter: (a, b) => new Date(a.ENDDATE).getTime() - new Date(b.ENDDATE).getTime(),
ellipsis: true,
hideInSearch: true,
}
@ -280,7 +294,7 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
expandable={{
expandRowByClick: true
}}
scroll={{ x: "100%", y: "calc(100vh - 410px)" }}
scroll={{ x: "100%", y: "calc(100vh - 430px)" }}
headerTitle={<PageTitleBox props={props} />} // 列表表头
search={{ span: 6 }}
request={async (params, sorter) => {
@ -298,10 +312,14 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
DEPT_IDS: selectedId,
CREATEDATE_Start: params.CREATEDATE_Start || "",
CREATEDATE_End: params.CREATEDATE_End || "",
ACCEPT_CODE: "200300",
SearchOtherKeyName: "COMMODITY_NAME,COMMODITY_BARCODE",
SearchOtherKeyValue: params?.searchText || '',
},
PageIndex: 1,
PageSize: 999999,
sortstr: sortstr.length ? sortstr.toString() : "",
// sortstr: sortstr.length ? sortstr.toString() : "",
sortstr: "ENDDATE desc",
}
setSearchParams(params)
@ -330,16 +348,30 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(columns,
reqDetailList,
`退货流程统计${searchParams?.CREATEDATE_Start}-${searchParams?.CREATEDATE_End}`,
{
topTitle: `退货流程统计表`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel
@ -351,7 +383,7 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => {
</div>
{/* 退货单 */}
<ReturnGoodsTable showDetail={onShow} parentRow={currentRow} onCencel={handleCloseModal} />
<ReturnGoodsTable showDetail={onShow} parentRow={currentRow} onCencel={handleCloseModal} searchParams={searchParams} />
</div>
)
}

View File

@ -7,14 +7,16 @@ import Draggable from "react-draggable";
import ProTable, { ActionType } from "@ant-design/pro-table";
import ReactHTMLTableToExcel from "react-html-table-to-excel";
import { handleGetRECEIVEDETAILSERVERPARTList, handleGetRECEIVESERVERPARTDetail } from "../../service";
import { highlightText } from "@/utils/highlightText";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
type DetailProps = {
onShow: boolean;
parentRow: any;
onCencel: any
searchParams?: any // 如果需要高亮查询文字 这个就有值
}
const warehouseInfo = ({ onShow, parentRow, onCencel }: DetailProps) => {
const warehouseInfo = ({ onShow, parentRow, onCencel, searchParams }: DetailProps) => {
const actionRef = useRef<ActionType>();
const formRef = useRef<FormInstance>();
const draggleRef = React.createRef<any>()
@ -65,6 +67,9 @@ const warehouseInfo = ({ onShow, parentRow, onCencel }: DetailProps) => {
width: 200,
ellipsis: true,
hideInSearch: true,
render: (_, record) => {
return highlightText(record?.COMMODITY_NAME, searchParams?.searchText)
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
@ -73,6 +78,9 @@ const warehouseInfo = ({ onShow, parentRow, onCencel }: DetailProps) => {
width: 120,
ellipsis: true,
hideInSearch: true,
render: (_, record) => {
return highlightText(record?.COMMODITY_BARCODE, searchParams?.searchText)
}
},
{
title: <div style={{ textAlign: 'center' }}></div>,
@ -336,16 +344,30 @@ const warehouseInfo = ({ onShow, parentRow, onCencel }: DetailProps) => {
loading={showLoading}
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(columns,
reqDetailList,
`${parentRow?.SERVERPART_NAME}入库验收单${parentRow?.RECEIVECENTER_DATE || ""}`,
{
topTitle: `${currentModalDetail?.SERVERPART_NAME || ''}${currentModalDetail?.SHOPNAME || ''}入库单`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel

View File

@ -23,6 +23,7 @@ import shop from "@/pages/BussinessProject/shop";
import { getMyShopList } from "@/pages/account/center/sevice";
import { handleGetRECEIVESERVERPARTList } from "../service";
import WarehouseInfo from "./components/warehouseInfo";
import { exportXlsxFromProColumnsExcelJS } from "@/utils/exportExcelFun";
const { Text } = Typography;
@ -111,7 +112,7 @@ const shopProcurement: React.FC<{ currentUser: CurrentUser }> = (props) => {
title: '查询内容',
hideInTable: true,
fieldProps: {
placeholder: "请输入商品名称/入库单号"
placeholder: "请输入商品名称/商品条码"
}
},
{
@ -404,11 +405,13 @@ const shopProcurement: React.FC<{ currentUser: CurrentUser }> = (props) => {
SERVERPARTSHOP_IDS: selectedId,
RECEIVECENTER_DATE_Start: params?.RECEIVECENTER_DATE_Start || "",
RECEIVECENTER_DATE_End: params?.RECEIVECENTER_DATE_End || "",
SearchOtherKeyName: "COMMODITY_NAME,COMMODITY_BARCODE",
SearchOtherKeyValue: params?.searchText || '',
},
keyWord: {
Key: 'RECEIVECENTER_CODE',
Value: params?.searchText || ''
},
// keyWord: {
// Key: 'RECEIVECENTER_CODE',
// Value: params?.searchText || ''
// },
PageIndex: 1,
PageSize: 999999,
sortstr: sortstr.length ? sortstr.toString() : "",
@ -452,16 +455,30 @@ const shopProcurement: React.FC<{ currentUser: CurrentUser }> = (props) => {
type="primary"
onClick={(e) => {
if (reqDetailList && reqDetailList.length > 0) {
setShowLoading(true)
setTimeout(() => {
setShowExportTable(true)
setTimeout(() => {
exportTable(e)
}, 100)
}, 100)
// 尝试一下 导出新方法
exportXlsxFromProColumnsExcelJS(columns,
reqDetailList,
`商品入库统计表${searchParams?.RECEIVECENTER_DATE_Start}-${searchParams?.RECEIVECENTER_DATE_End}`,
{
topTitle: `商品入库统计表`, // 顶部大标题
}
)
} else {
message.error('暂无数据可导出!')
}
// if (reqDetailList && reqDetailList.length > 0) {
// setShowLoading(true)
// setTimeout(() => {
// setShowExportTable(true)
// setTimeout(() => {
// exportTable(e)
// }, 100)
// }, 100)
// } else {
// message.error('暂无数据可导出!')
// }
}}
>
excel
@ -529,8 +546,8 @@ const shopProcurement: React.FC<{ currentUser: CurrentUser }> = (props) => {
<WarehouseInfo onShow={showDetail} parentRow={currentRow} onCencel={handleGetClose} />
{/* 说是要里面高亮 查询的内容 那么就要传入 searchParams */}
<WarehouseInfo onShow={showDetail} parentRow={currentRow} onCencel={handleGetClose} searchParams={searchParams} />
</div>
)
}

View File

@ -251,8 +251,6 @@ const ConvenienceStoreProductReview: React.FC<{ currentUser: CurrentUser }> = (p
if (selectModalRowList && selectModalRowList.length > 0) {
// 商品勾选的行
let list: any = JSON.parse(JSON.stringify(selectedModalOrderRowKeys))
console.log('listlistlist', list);
console.log('tableDatatableDatatableData', tableData);
let reqList: any = []
// 判断是不是所有的价格都已经输入了
let isAllOk: boolean = true
@ -261,7 +259,7 @@ const ConvenienceStoreProductReview: React.FC<{ currentUser: CurrentUser }> = (p
tableData.forEach((item: any) => {
if (list.indexOf(item.COMMODITY_ID.toString()) !== -1) {
if (item.COMMODITY_TYPE === '普通商品') {
item.COMMODITY_TYPE = 1012
item.COMMODITY_TYPE = 1903
}
reqList.push(item)
}
@ -283,10 +281,7 @@ const ConvenienceStoreProductReview: React.FC<{ currentUser: CurrentUser }> = (p
let req: any = {
list: reqList
}
console.log('reqreqreqreq', req);
const data = await handleApproveCommodityInfo_AHJG(req)
console.log('datadatadatadata', data);
if (data.Result_Code === 100) {
message.success(data.Result_Desc)

296
src/utils/exportExcelFun.ts Normal file
View File

@ -0,0 +1,296 @@
// exportExcel.ts
import ExcelJS from 'exceljs';
/** ======== 列类型(按需裁剪) ======== */
type AnyCol = {
title?: any;
dataIndex?: string | (string | number)[];
children?: AnyCol[];
valueEnum?: Record<string | number, { text?: string } | string>;
renderText?: (text: any, record: any, index: number) => any;
hideInTable?: boolean;
valueType?: 'index' | string;
};
/** ========== 新增:拍平树形数据 ========== */
function flattenTree<T extends Record<string, any>>(
list: T[] = [],
childrenKey = 'children',
out: T[] = []
): T[] {
for (const node of list) {
// 先推当前节点(浅拷贝去掉 children避免把对象树写入单元格
const { [childrenKey]: kids, ...rest } = node as any;
out.push(rest as T);
if (Array.isArray(kids) && kids.length) {
flattenTree(kids, childrenKey, out);
}
}
return out;
}
/** 抽取 React 节点文本 */
function extractText(node: any): string {
if (node == null || node === false) return '';
if (typeof node === 'string' || typeof node === 'number') return String(node);
const children = node?.props?.children;
if (Array.isArray(children)) return children.map(extractText).join('');
if (children != null) return extractText(children);
const html = node?.props?.dangerouslySetInnerHTML?.__html;
if (typeof html === 'string') return html.replace(/<[^>]+>/g, '');
return String(node ?? '');
}
/** dataIndex 路径取值 */
const toPath = (di?: AnyCol['dataIndex']): (string | number)[] =>
Array.isArray(di) ? di : (typeof di === 'string' ? di.split('.') : []);
const getByPath = (obj: any, path: (string | number)[]) =>
path.reduce((acc, k) => (acc == null ? acc : acc[k]), obj);
/** 过滤 hideInTable父级联动 */
function pruneHiddenColumns(cols: AnyCol[]): AnyCol[] {
const walk = (arr: AnyCol[]): AnyCol[] =>
(arr || [])
.filter(col => !col?.hideInTable)
.map(col => {
if (col.children?.length) {
const kids = walk(col.children);
if (!kids.length) return null as any;
return { ...col, children: kids };
}
return col;
})
.filter(Boolean);
return walk(cols);
}
/** 叶子列(有 dataIndex 的) */
const getLeaves = (cols: AnyCol[]): AnyCol[] => {
const out: AnyCol[] = [];
const walk = (arr: AnyCol[]) => {
arr.forEach(c => {
if (c?.children?.length) {
walk(c.children!);
} else if (c && (c.dataIndex || c.valueType === 'index')) { // << 修改
out.push(c);
}
});
};
walk(cols);
return out;
};
/** 深度和列跨度 */
const getDepth = (cols: AnyCol[]): number => {
const dfs = (c: AnyCol): number =>
c.children?.length ? 1 + Math.max(...c.children.map(dfs)) : 1;
return Math.max(...cols.map(dfs));
};
const getColSpan = (c: AnyCol): number =>
c.children?.length ? c.children.map(getColSpan).reduce((a, b) => a + b, 0) : 1;
/** 构造多级表头矩阵 & 合并信息(不含顶部大标题/信息行) */
function buildHeaderMatrix(cols: AnyCol[]) {
const depth = getDepth(cols);
const rows: string[][] = Array.from({ length: depth }, () => []);
let colCursor = 0;
const merges: Array<{ r1: number; c1: number; r2: number; c2: number }> = [];
const place = (list: AnyCol[], level: number) => {
list.forEach(col => {
const span = getColSpan(col);
const rowSpan = col.children?.length ? 1 : depth - level;
const title = extractText(col.title ?? '');
rows[level][colCursor] = title;
for (let i = 1; i < span; i++) rows[level][colCursor + i] = '';
if (span > 1 || rowSpan > 1) {
merges.push({ r1: level + 1, c1: colCursor + 1, r2: level + rowSpan, c2: colCursor + span });
}
if (col.children?.length) {
place(col.children, level + 1);
} else {
colCursor += 1;
}
});
};
place(cols, 0);
const maxLen = Math.max(...rows.map(r => r.length));
rows.forEach(r => { for (let i = 0; i < maxLen; i++) if (typeof r[i] === 'undefined') r[i] = ''; });
return { headerAOA: rows, merges, depth, columnCount: maxLen };
}
/** 单元格显示值valueEnum / renderText / 原始值) */
function getCellValue(col: AnyCol, record: any, rowIndex: number) {
// << 新增:序号列
if (col.valueType === 'index') return rowIndex + 1;
const raw = getByPath(record, toPath(col.dataIndex));
if (col.valueEnum) {
const ve = col.valueEnum[raw as any];
if (typeof ve === 'string') return ve;
if (ve?.text != null) return ve.text;
}
if (col.renderText) {
try { return col.renderText(raw, record, rowIndex); } catch { }
}
return raw;
}
/** 估算列宽(简单) */
const estimateWidth = (v: any) => {
const s = (v ?? '').toString();
const len = Array.from(s).reduce((n, ch) => n + (/[^\x00-\xff]/.test(ch) ? 2 : 1), 0);
return Math.min(Math.max(len + 2, 8), 60);
};
export async function exportXlsxFromProColumnsExcelJS(
rawColumns: AnyCol[],
dataSource: any[],
filename?: string,
options?: {
sheetName?: string;
chunkSize?: number;
topTitle?: string; // 顶部大标题(整表合并 + 居中)
infoRowLeft?: string; // 标题下插入的左侧文字
infoRowRight?: string; // 标题下插入的右侧文字(右对齐)
freezeHeader?: boolean; // 冻结到哪一行(自动计算)
childrenKey?: string;
}
) {
const {
sheetName = '数据',
chunkSize = 100_000,
topTitle,
infoRowLeft,
infoRowRight,
freezeHeader = true,
childrenKey = 'children',
} = options || {};
// === 新增:拍平树形数据 ===
const flatData = flattenTree<any>(Array.isArray(dataSource) ? dataSource : [], childrenKey);
const columns = pruneHiddenColumns(rawColumns);
const leafCols = getLeaves(columns);
if (!leafCols.length) throw new Error('无可导出的列(可能被 hideInTable 全部隐藏)');
const { headerAOA, merges, columnCount } = buildHeaderMatrix(columns);
const wb = new ExcelJS.Workbook();
wb.created = new Date();
wb.modified = new Date();
const total = flatData?.length ?? 0;
const totalSheets = Math.max(1, Math.ceil(total / chunkSize));
for (let si = 0; si < totalSheets; si++) {
const ws = wb.addWorksheet(totalSheets === 1 ? sheetName : `${sheetName}_${si + 1}`, {
views: [{ state: 'frozen' }],
});
// 1) 顶部大标题(可选)
let currentRowIndex = 1;
if (topTitle) {
const row = ws.getRow(currentRowIndex);
// 写入一个单元格,再合并整行
row.getCell(1).value = topTitle;
ws.mergeCells(currentRowIndex, 1, currentRowIndex, columnCount);
// 居中 + 加粗 + 较大字号
const cell = ws.getCell(currentRowIndex, 1);
cell.alignment = { horizontal: 'center', vertical: 'middle' };
cell.font = { bold: true, size: 14 };
row.height = 22;
currentRowIndex += 1;
}
// 2) 信息行(可选,左右结构)
if (infoRowLeft != null || infoRowRight != null) {
const row = ws.getRow(currentRowIndex);
// 左侧:合并 1..(columnCount/2),左对齐
const split = Math.max(1, Math.floor(columnCount / 2));
if (infoRowLeft != null) {
row.getCell(1).value = infoRowLeft;
ws.mergeCells(currentRowIndex, 1, currentRowIndex, split);
const leftCell = ws.getCell(currentRowIndex, 1);
leftCell.alignment = { horizontal: 'left', vertical: 'middle' };
leftCell.font = { size: 11 };
}
// 右侧:合并 (split+1)..columnCount右对齐
if (infoRowRight != null) {
row.getCell(split + 1).value = infoRowRight;
ws.mergeCells(currentRowIndex, split + 1, currentRowIndex, columnCount);
const rightCell = ws.getCell(currentRowIndex, split + 1);
rightCell.alignment = { horizontal: 'right', vertical: 'middle' };
rightCell.font = { size: 11 };
}
row.height = 18;
currentRowIndex += 1;
}
// 3) 多级表头(全部居中 + 加粗)
const headerStartRow = currentRowIndex;
headerAOA.forEach((r, idx) => {
const row = ws.getRow(headerStartRow + idx);
r.forEach((v, cIdx) => {
const cell = row.getCell(cIdx + 1);
cell.value = v;
cell.alignment = { horizontal: 'center', vertical: 'middle' };
cell.font = { bold: true };
});
row.height = 18;
});
// 应用表头合并
merges.forEach(m => {
ws.mergeCells(headerStartRow + (m.r1 - 1), m.c1, headerStartRow + (m.r2 - 1), m.c2);
});
currentRowIndex += headerAOA.length;
// 4) 数据(从 currentRowIndex 开始)
const start = si * chunkSize;
const end = Math.min(start + chunkSize, total);
const batch = flatData.slice(start, end);
for (let i = 0; i < batch.length; i++) {
const rec = batch[i];
const row = ws.getRow(currentRowIndex + i);
leafCols.forEach((col, j) => {
// 这里 rowIndex 仍然传全局行号 start + i序号列会自动正确
row.getCell(j + 1).value = getCellValue(col, rec, start + i);
});
// 适度让出主线程ExcelJS 是纯 JS通常也很稳如需进一步优化可用 setTimeout 分批
}
currentRowIndex += batch.length;
// 5) 列宽:基于表头 + 采样数据估算
const sampleRows = Math.min(batch.length, 200);
for (let c = 1; c <= columnCount; c++) {
const headerMax = Math.max(...headerAOA.map(r => estimateWidth(r[c - 1])));
let dataMax = 8;
for (let i = 0; i < sampleRows; i++) {
const v = leafCols[c - 1] ? getCellValue(leafCols[c - 1], batch[i], start + i) : '';
dataMax = Math.max(dataMax, estimateWidth(v));
}
ws.getColumn(c).width = Math.max(headerMax, dataMax);
}
// 6) 冻结窗格:冻结到(标题 + 信息行 + 表头)这一行的下一行
if (freezeHeader) {
// const freezeRow = (topTitle ? 1 : 0) + (infoRowLeft != null || infoRowRight != null ? 1 : 0) + headerAOA.length;
// ws.views = [{ state: 'frozen', ySplit: freezeRow }];
}
}
// 生成并下载(浏览器环境)
const buf = await wb.xlsx.writeBuffer();
const blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${filename}.xlsx`;
a.click();
URL.revokeObjectURL(url);
}

View File

@ -1,4 +1,4 @@
// 由 scripts/writeVersion.js 自动生成
export const VERSION = "4.5.42";
export const GIT_HASH = "a05dd91";
export const BUILD_TIME = "2025-09-01T07:45:38.579Z";
export const VERSION = "4.5.45";
export const GIT_HASH = "d3a4c2a";
export const BUILD_TIME = "2025-09-02T10:19:24.187Z";