diff --git a/package.json b/package.json index 97ff304..c4e30e4 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/pages/ahjgPage/InventoryCategory/index.tsx b/src/pages/ahjgPage/InventoryCategory/index.tsx index 1db804d..b5b338a 100644 --- a/src/pages/ahjgPage/InventoryCategory/index.tsx +++ b/src/pages/ahjgPage/InventoryCategory/index.tsx @@ -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" /> , @@ -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 diff --git a/src/pages/ahjgPage/InventoryDetails/components/COMMODITYINFO.tsx b/src/pages/ahjgPage/InventoryDetails/components/COMMODITYINFO.tsx index 80b075f..4ac5969 100644 --- a/src/pages/ahjgPage/InventoryDetails/components/COMMODITYINFO.tsx +++ b/src/pages/ahjgPage/InventoryDetails/components/COMMODITYINFO.tsx @@ -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, diff --git a/src/pages/ahjgPage/InventoryDetails/index.tsx b/src/pages/ahjgPage/InventoryDetails/index.tsx index 4da0ec7..859c26f 100644 --- a/src/pages/ahjgPage/InventoryDetails/index.tsx +++ b/src/pages/ahjgPage/InventoryDetails/index.tsx @@ -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 diff --git a/src/pages/ahjgPage/inventoryInformation/components/inventoryDetail.tsx b/src/pages/ahjgPage/inventoryInformation/components/inventoryDetail.tsx index f165730..bf6d33e 100644 --- a/src/pages/ahjgPage/inventoryInformation/components/inventoryDetail.tsx +++ b/src/pages/ahjgPage/inventoryInformation/components/inventoryDetail.tsx @@ -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(); const formRef = useRef(); const draggleRef = React.createRef() @@ -84,7 +86,7 @@ const inventoryDetail = ({ onShow, onCancel, parentRow, currentUser, ServerpartI setCurrentRow(record) setShowDetail(true) }}> - {record?.COMMODITY_NAME} + {highlightText(record?.COMMODITY_NAME, searchParams?.searchText)} : "" } }, @@ -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:
商品编码
, + dataIndex: "COMMODITY_BRAND", + align: 'left', + width: 120, + ellipsis: true, + hideInSearch: true, + render: (_, record) => { + return record?.COMMODITY_BRAND ? highlightText(record?.COMMODITY_BRAND, searchParams?.searchText) : "-" + } }, { title:
规格
, diff --git a/src/pages/ahjgPage/inventoryInformation/index.tsx b/src/pages/ahjgPage/inventoryInformation/index.tsx index 3a31d7f..bead50a 100644 --- a/src/pages/ahjgPage/inventoryInformation/index.tsx +++ b/src/pages/ahjgPage/inventoryInformation/index.tsx @@ -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(); const formRef = useRef(); const [reqDetailList, setReqDetailList] = useState(); // 合计项数据源 + const [reqDefaultDetailList, setReqDefaultDetailList] = useState(); // 合计项数据源 const [printOut, setPrintOut] = useState(); // 打印数据的内容 const [collapsible, setCollapsible] = useState(false) const [treeView, setTreeView] = useState() @@ -53,6 +56,10 @@ const inventoryInformation: React.FC<{ currentUser: CurrentUser }> = (props) => const [showDetail, setShowDetail] = useState(false) // 当前选中的服务区id const [currentSelectServerpartId, setCurrentSelectServerpartId] = useState() + // 判断是不是点击了 排序、分页等 + const isNoTableReload = useRef(false) + // 表格数据 + const [tableData, setTableData] = useState() 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)} : "-" } }, @@ -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:
商品编码
, + dataIndex: "COMMODITY_BRAND", + align: 'left', + width: 120, + ellipsis: true, + hideInSearch: true, + render: (_, record) => { + return record?.COMMODITY_BRAND ? highlightText(record?.COMMODITY_BRAND, searchParams?.searchText) : "-" + } }, - // { - // title:
商品编码
, - // dataIndex: "COMMODITY_BRAND", - // align: 'left', - // width: 120, - // ellipsis: true, - // hideInSearch: true, - // }, { title:
商品规格
, 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:
含税销售金额
, 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 (
{ @@ -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: [ @@ -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) => ) }} + 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) + } + }} />
{/* 库存的详情信息 */} - + ) diff --git a/src/pages/ahjgPage/oneProductMultipleSizes/index.tsx b/src/pages/ahjgPage/oneProductMultipleSizes/index.tsx index 422fa02..4054795 100644 --- a/src/pages/ahjgPage/oneProductMultipleSizes/index.tsx +++ b/src/pages/ahjgPage/oneProductMultipleSizes/index.tsx @@ -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 diff --git a/src/pages/ahjgPage/purchaseReceiving/index.tsx b/src/pages/ahjgPage/purchaseReceiving/index.tsx index 1bbd560..52ceb3a 100644 --- a/src/pages/ahjgPage/purchaseReceiving/index.tsx +++ b/src/pages/ahjgPage/purchaseReceiving/index.tsx @@ -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(); const formRef = useRef(); const [reqDetailList, setReqDetailList] = useState(); // 合计项数据源 + const [reqDefaultDetailList, setReqDefaultDetailList] = useState(); // 合计项数据源 const [printOut, setPrintOut] = useState(); // 打印数据的内容 const [collapsible, setCollapsible] = useState(false) const [treeView, setTreeView] = useState() @@ -39,6 +41,10 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => { const [searchParams, setSearchParams] = useState() // 拿到服务区下的所有门店id集合 const [handleAllShopId, sethandleAllShopId] = useState() + // 判断是不是点击了 排序、分页等 + const isNoTableReload = useRef(false) + // 表格数据 + const [tableData, setTableData] = useState() 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:
采购总数量
, 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:
门店名称
, + dataIndex: "ServerpartShop_Name", + align: 'center', + width: 150, + ellipsis: true, + hideInSearch: true, }, ] + type SortState = { field?: string; order?: 'ascend' | 'descend' }; + type KeyOf = Extract; + + interface SortTreeOptions { + childrenKey?: KeyOf; // 子节点字段名,默认 'children' + } + + /** 树形排序:父与父排,父内的子与子排(递归) */ + const sortListByState = >( + list: T[] = [], + state?: SortState, + options?: SortTreeOptions + ): 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: 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 ( +
+ {record?.Supplier_Name}-{record?.Serverpart_Name}-{record?.ServerpartShop_Name}合计 +
+ ); + }; + 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 (
{ // 打印报表 @@ -283,7 +438,7 @@ const purchaseReceiving: React.FC<{ currentUser: CurrentUser }> = (props) => { { showExportTable && reqDetailList && reqDetailList.length > 0 ? = (props) => { scroll={{ x: "100%", y: "calc(100vh - 410px)" }} headerTitle={} // 列表表头 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 ] }} + 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) + } + }} />
diff --git a/src/pages/ahjgPage/returnProcess/components/returnGoodsTable.tsx b/src/pages/ahjgPage/returnProcess/components/returnGoodsTable.tsx index 3a988d8..d5b0f50 100644 --- a/src/pages/ahjgPage/returnProcess/components/returnGoodsTable.tsx +++ b/src/pages/ahjgPage/returnProcess/components/returnGoodsTable.tsx @@ -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(); const formRef = useRef(); const draggleRef = React.createRef() @@ -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:
商品名称
, - 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:
供应商名
, - width: 150, + width: 250, dataIndex: "SUPPLIER_NAME", align: 'left', ellipsis: true }, { title:
退货数量
, - width: 120, + width: 100, dataIndex: "BACK_COUNT", valueType: 'digit', align: 'right', @@ -83,14 +91,14 @@ const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => { }, { title:
税率
, - width: 120, + width: 100, dataIndex: "DUTY_PARAGRAPH", align: 'right', ellipsis: true }, { title:
含税进价
, - width: 120, + width: 100, dataIndex: "PURCHASE_TAXPRICE", valueType: 'digit', align: 'right', @@ -98,7 +106,7 @@ const ReturnGoodsTable = ({ showDetail, parentRow, onCencel }: DetailProps) => { }, { title:
含税金额
, - 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:
退货原由
, + 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) { diff --git a/src/pages/ahjgPage/returnProcess/index.tsx b/src/pages/ahjgPage/returnProcess/index.tsx index 58d9a03..2a4b773 100644 --- a/src/pages/ahjgPage/returnProcess/index.tsx +++ b/src/pages/ahjgPage/returnProcess/index.tsx @@ -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:
退货数量
, - 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 { @@ -138,40 +140,50 @@ const returnProcess: React.FC<{ currentUser: CurrentUser }> = (props) => { setCurrentRow(record) setOnShow(true) }}> - 1 + {record?.OPERATE_COUNT || ""} } }, { title:
合计含税进价
, - 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:
合计除税进价
, + 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={} // 列表表头 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) => { {/* 退货单 */} - + ) } diff --git a/src/pages/ahjgPage/shopProcurement/components/warehouseInfo.tsx b/src/pages/ahjgPage/shopProcurement/components/warehouseInfo.tsx index 359df7b..fe583f7 100644 --- a/src/pages/ahjgPage/shopProcurement/components/warehouseInfo.tsx +++ b/src/pages/ahjgPage/shopProcurement/components/warehouseInfo.tsx @@ -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(); const formRef = useRef(); const draggleRef = React.createRef() @@ -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:
商品条码
, @@ -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:
商品规格
, @@ -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 diff --git a/src/pages/ahjgPage/shopProcurement/index.tsx b/src/pages/ahjgPage/shopProcurement/index.tsx index 8b31d02..d321fcc 100644 --- a/src/pages/ahjgPage/shopProcurement/index.tsx +++ b/src/pages/ahjgPage/shopProcurement/index.tsx @@ -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) => { - - + {/* 说是要里面高亮 查询的内容 那么就要传入 searchParams */} + ) } diff --git a/src/pages/reports/productControl/ConvenienceStoreProductReview/index.tsx b/src/pages/reports/productControl/ConvenienceStoreProductReview/index.tsx index 0104ebb..ade0813 100644 --- a/src/pages/reports/productControl/ConvenienceStoreProductReview/index.tsx +++ b/src/pages/reports/productControl/ConvenienceStoreProductReview/index.tsx @@ -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) diff --git a/src/utils/exportExcelFun.ts b/src/utils/exportExcelFun.ts new file mode 100644 index 0000000..8521675 --- /dev/null +++ b/src/utils/exportExcelFun.ts @@ -0,0 +1,296 @@ +// exportExcel.ts +import ExcelJS from 'exceljs'; + +/** ======== 列类型(按需裁剪) ======== */ +type AnyCol = { + title?: any; + dataIndex?: string | (string | number)[]; + children?: AnyCol[]; + valueEnum?: Record; + renderText?: (text: any, record: any, index: number) => any; + hideInTable?: boolean; + valueType?: 'index' | string; +}; + +/** ========== 新增:拍平树形数据 ========== */ +function flattenTree>( + 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(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); +} \ No newline at end of file diff --git a/src/versionEnv.ts b/src/versionEnv.ts index b4b0cc7..bcee452 100644 --- a/src/versionEnv.ts +++ b/src/versionEnv.ts @@ -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";