update
This commit is contained in:
parent
d3a4c2ad28
commit
30fa7f3f4c
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
296
src/utils/exportExcelFun.ts
Normal 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);
|
||||
}
|
||||
@ -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";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user