diff --git a/dist.zip b/dist.zip
index 28a2741..55849be 100644
Binary files a/dist.zip and b/dist.zip differ
diff --git a/package.json b/package.json
index fc4d8ed..01474aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ant-design-pro",
- "version": "4.5.128",
+ "version": "4.5.131",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
diff --git a/src/pages/travelMember/BookingMealOrder/components/orderDetailModal.tsx b/src/pages/travelMember/BookingMealOrder/components/orderDetailModal.tsx
index a29a0db..158b177 100644
--- a/src/pages/travelMember/BookingMealOrder/components/orderDetailModal.tsx
+++ b/src/pages/travelMember/BookingMealOrder/components/orderDetailModal.tsx
@@ -787,7 +787,7 @@ const OrderDetailModal = ({ modalVisible, handleCloseModal, currentRow, detailTy
}
物流信息
- }
- size="small"
- onClick={addLogistics}
- style={{ borderStyle: 'dashed' }}
- >
- 添加物流信息
-
+ {
+ Number(currentRow?.SALEBILL_STATE) >= 3000 ? '' :
+ }
+ size="small"
+ onClick={addLogistics}
+ style={{ borderStyle: 'dashed' }}
+ >
+ 添加物流信息
+
+ }
{logisticsList.map((logistics, index) => (
@@ -859,16 +862,19 @@ const OrderDetailModal = ({ modalVisible, handleCloseModal, currentRow, detailTy
- }
- onClick={() => removeLogistics(logistics.id)}
- style={{ marginTop: '24px' }}
- disabled={logisticsList.length === 1}
- >
- 删除
-
+ {
+ Number(currentRow?.SALEBILL_STATE) >= 3000 ? '' :
+ }
+ onClick={() => removeLogistics(logistics.id)}
+ style={{ marginTop: '24px' }}
+ disabled={logisticsList.length === 1}
+ >
+ 删除
+
+ }
diff --git a/src/pages/travelMember/MallOrderManage/index.tsx b/src/pages/travelMember/MallOrderManage/index.tsx
index 6e6d980..cc90f67 100644
--- a/src/pages/travelMember/MallOrderManage/index.tsx
+++ b/src/pages/travelMember/MallOrderManage/index.tsx
@@ -137,7 +137,7 @@ const MallOrderManage: React.FC<{ currentUser: CurrentUser, isComponent?: boolea
"0": "全部",
"3000": "零售商城",
"3001": "工会商城",
- "3002": "品诺商城",
+ // "3002": "品诺商城",
"3010": "积分商城"
},
initialValue: '0',
@@ -315,7 +315,7 @@ const MallOrderManage: React.FC<{ currentUser: CurrentUser, isComponent?: boolea
valueEnum: {
"3000": "零售商城",
"3001": "工会商城",
- "3002": "品诺商城",
+ // "3002": "品诺商城",
"3010": "积分商城"
},
// valueEnum: {
@@ -424,40 +424,41 @@ const MallOrderManage: React.FC<{ currentUser: CurrentUser, isComponent?: boolea
// }
// }
// },
- // {
- // title: '供应商',
- // dataIndex: "MERCHANTS_IDS",
- // valueType: 'select',
- // request: async () => {
- // const req = {
- // searchParameter: {
- // OWNERUNIT_ID: currentUser?.OwnerUnitId,
- // PROVINCE_CODE: currentUser?.ProvinceCode,
- // MERCHANTS_TYPE: ""
- // },
- // PageIndex: 1,
- // PageSize: 999999,
- // }
- // const data = await handeGetMERCHANTSList(req);
- // return data.List
- // },
- // hideInTable: true,
- // fieldProps: {
- // allowClear: true,
- // showSearch: true,
- // filterTreeNode: (input, node) => {
- // // ✅ 输入时根据 AUTOTYPE_NAME 模糊匹配
- // return node?.MERCHANTS_NAME?.toLowerCase()?.includes(input.toLowerCase());
- // },
- // treeDefaultExpandAll: true,
- // fieldNames: {
- // label: 'MERCHANTS_NAME',
- // value: 'MERCHANTS_ID',
- // },
- // disabled: currentUser?.UserPattern === 4000
- // },
- // initialValue: currentUser?.UserPattern === 4000 ? currentUser?.SupplierID : ""
- // },
+ {
+ title: '供应商',
+ dataIndex: "MERCHANTS_IDS",
+ valueType: 'select',
+ request: async () => {
+ const req = {
+ searchParameter: {
+ OWNERUNIT_ID: currentUser?.OwnerUnitId,
+ PROVINCE_CODE: currentUser?.ProvinceCode,
+ MERCHANTS_TYPE: ""
+ },
+ PageIndex: 1,
+ PageSize: 999999,
+ }
+ const data = await handeGetMERCHANTSList(req);
+ return data.List
+ },
+ hideInTable: true,
+ fieldProps: {
+ allowClear: true,
+ showSearch: true,
+ filterTreeNode: (input, node) => {
+ // ✅ 输入时根据 AUTOTYPE_NAME 模糊匹配
+ return node?.MERCHANTS_NAME?.toLowerCase()?.includes(input.toLowerCase());
+ },
+ treeDefaultExpandAll: true,
+ fieldNames: {
+ label: 'MERCHANTS_NAME',
+ value: 'MERCHANTS_ID',
+ },
+ disabled: currentUser?.UserPattern === 4000
+ },
+ hideInSearch: currentUser?.UserPattern === 4000,
+ // initialValue: currentUser?.UserPattern === 4000 ? currentUser?.SupplierID : ""
+ },
{
title: '单位名称',
dataIndex: "COMPANY_IDS",
@@ -789,15 +790,17 @@ const MallOrderManage: React.FC<{ currentUser: CurrentUser, isComponent?: boolea
// 跟交易台账保持一致的 导出Excel
const handleGetExportData = async () => {
+ console.log('searchParams', searchParams);
+
setGetExportDataLoading(true)
const req: any = {
ExportType: 1,
OwnerUnitId: "911",
- CompanyId: searchParams?.CompanyId || "",
- MerchantId: currentUser?.SupplierID || "",
- SaleBillState: searchParams?.OrderStatus === '0' ? '' : (searchParams?.OrderStatus || ""),
- SaleBillType: searchParams?.OrderType === '0' ? '' : (searchParams?.OrderType || ""),
- ChannelType: searchParams?.PaymentMethod || "",
+ CompanyId: searchParams?.COMPANY_IDS || "",
+ MerchantId: currentUser?.UserPattern === 4000 ? currentUser?.SupplierID : searchParams?.MERCHANTS_IDS || "",
+ SaleBillState: searchParams?.orderStatus === '0' ? '' : (searchParams?.orderStatus || ""),
+ SaleBillType: searchParams?.orderType === '0' ? '' : (searchParams?.orderType || ""),
+ ChannelType: searchParams?.CHANNEL_TYPE === '0' ? '' : searchParams?.CHANNEL_TYPE || "",
StartDate: searchParams?.ORDER_DATE_Start || "",
EndDate: searchParams?.ORDER_DATE_End || "",
// SearchKeyName: "SupplierName,CommodityName,OrderCode",
diff --git a/src/pages/travelMember/TradingLedger/index.tsx b/src/pages/travelMember/TradingLedger/index.tsx
index a1bf202..ddd99f2 100644
--- a/src/pages/travelMember/TradingLedger/index.tsx
+++ b/src/pages/travelMember/TradingLedger/index.tsx
@@ -14,7 +14,7 @@ import ProTable from "@ant-design/pro-table";
import ReactHTMLTableToExcel from "react-html-table-to-excel";
import LeftSelectTree from "@/pages/reports/settlementAccount/component/leftSelectTree";
import PageTitleBox from "@/components/PageTitleBox";
-import { handeGetCOMPANYList, handeGetOnlineBillAccountList, handeGetSalebillAccountList, handeGetSupplierSaleBillList } from "../service";
+import { handeGetCOMPANYList, handeGetMERCHANTSList, handeGetOnlineBillAccountList, handeGetSalebillAccountList, handeGetSupplierSaleBillList } from "../service";
import moment from 'moment'
import OrderDetailModal from "../BookingMealOrder/components/orderDetailModal";
import { exportXlsxFromProColumnsExcelJS, formatTreeData, handleSetlogSave } from "@/utils/format";
@@ -78,17 +78,6 @@ const TradingLedger: React.FC<{ currentUser: CurrentUser }> = (props) => {
placeholder: "请输入供货商/购买的商品/订单编号/会员名称/电话号码"
}
},
- {
- dataIndex: "CompanyId",
- title: "所属单位",
- hideInTable: true,
- valueType: 'select',
- fieldProps: {
- showSearch: true,
- options: companyList,
- filterOption: (input: any, option: any) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase()),
- }
- },
{
title: '下单时间',
dataIndex: 'search_date',
@@ -114,6 +103,98 @@ const TradingLedger: React.FC<{ currentUser: CurrentUser }> = (props) => {
// initialValue: [moment().subtract(1, 'M').format('YYYY-MM-DD'), moment().format('YYYY-MM-DD')],
initialValue: [moment().startOf('M'), moment()],
},
+ {
+ title: "订单状态",
+ dataIndex: "OrderStatus",
+ valueType: "select",
+ valueEnum: {
+ "0": "全部",
+ "1005": "订单待支付",
+ "1010": "订单待发货",
+ "2010": "订单已发货",
+ "3000": "订单已完成",
+ "8000": "退款申请中",
+ "8900": "订单已退款",
+ "9000": "订单已关闭",
+ "9999": "订单已撤销"
+ },
+ initialValue: '0',
+ hideInTable: true,
+ },
+ {
+ title: "订单类型",
+ dataIndex: "OrderType",
+ valueType: "select",
+ valueEnum: {
+ "0": "全部",
+ "3000": "零售商城",
+ "3001": "工会商城",
+ // "3002": "品诺商城",
+ "3010": "积分商城"
+ },
+ initialValue: '0',
+ hideInTable: true,
+ },
+
+ {
+ title: "支付方式",
+ dataIndex: "CHANNEL_TYPE",
+ valueType: "select",
+ valueEnum: {
+ "0": "全部",
+ "工会余额": "工会余额",
+ "组合支付": "组合支付",
+ "微信": "微信"
+ },
+ initialValue: '0',
+ hideInTable: true,
+ },
+ {
+ title: '供应商',
+ dataIndex: "MERCHANTS_IDS",
+ valueType: 'select',
+ request: async () => {
+ const req = {
+ searchParameter: {
+ OWNERUNIT_ID: currentUser?.OwnerUnitId,
+ PROVINCE_CODE: currentUser?.ProvinceCode,
+ MERCHANTS_TYPE: ""
+ },
+ PageIndex: 1,
+ PageSize: 999999,
+ }
+ const data = await handeGetMERCHANTSList(req);
+ return data.List
+ },
+ hideInTable: true,
+ fieldProps: {
+ allowClear: true,
+ showSearch: true,
+ filterTreeNode: (input, node) => {
+ // ✅ 输入时根据 AUTOTYPE_NAME 模糊匹配
+ return node?.MERCHANTS_NAME?.toLowerCase()?.includes(input.toLowerCase());
+ },
+ treeDefaultExpandAll: true,
+ fieldNames: {
+ label: 'MERCHANTS_NAME',
+ value: 'MERCHANTS_ID',
+ },
+ disabled: currentUser?.UserPattern === 4000
+ },
+ hideInSearch: currentUser?.UserPattern === 4000,
+ // initialValue: currentUser?.UserPattern === 4000 ? currentUser?.SupplierID : ""
+ },
+ {
+ dataIndex: "CompanyId",
+ title: "所属单位",
+ hideInTable: true,
+ valueType: 'select',
+ fieldProps: {
+ showSearch: true,
+ options: companyList,
+ filterOption: (input: any, option: any) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase()),
+ }
+ },
{
title: "序号",
dataIndex: "index",
@@ -226,6 +307,7 @@ const TradingLedger: React.FC<{ currentUser: CurrentUser }> = (props) => {
// "3002": "品诺商城",
"3010": "积分商城"
},
+ hideInSearch: true,
initialValue: '0',
},
{
@@ -305,6 +387,7 @@ const TradingLedger: React.FC<{ currentUser: CurrentUser }> = (props) => {
},
align: "center",
initialValue: '0',
+ hideInSearch: true
},
// {
// dataIndex: 'desc',
@@ -575,10 +658,10 @@ const TradingLedger: React.FC<{ currentUser: CurrentUser }> = (props) => {
ExportType: type,
OwnerUnitId: "911",
CompanyId: searchParams?.CompanyId || "",
- MerchantId: currentUser?.SupplierID || "",
- SaleBillState: searchParams?.OrderStatus === '0' ? '' : (searchParams?.OrderStatus || ""),
- SaleBillType: searchParams?.OrderType === '0' ? '' : (searchParams?.OrderType || ""),
- ChannelType: searchParams?.PaymentMethod || "",
+ MerchantId: currentUser?.UserPattern === 4000 ? currentUser?.SupplierID : searchParams?.MERCHANTS_IDS || "",
+ SaleBillState: searchParams?.orderStatus === '0' ? '' : (searchParams?.orderStatus || ""),
+ SaleBillType: searchParams?.orderType === '0' ? '' : (searchParams?.orderType || ""),
+ ChannelType: searchParams?.CHANNEL_TYPE === '0' ? '' : searchParams?.CHANNEL_TYPE || "",
StartDate: searchParams?.ORDER_DATE_Start || "",
EndDate: searchParams?.ORDER_DATE_End || "",
// SearchKeyName: "SupplierName,CommodityName,OrderCode",
@@ -695,16 +778,17 @@ const TradingLedger: React.FC<{ currentUser: CurrentUser }> = (props) => {
// SearchParameter: {
OwnerUnitId: "911",
CompanyId: params?.CompanyId || "",
- MerchantId: "",
+ MerchantId: currentUser?.UserPattern === 4000 ? currentUser?.SupplierID : params?.MERCHANTS_IDS || "",
SaleBillState: params?.OrderStatus === '0' ? '' : (params?.OrderStatus || ""),
SaleBillType: params?.OrderType === '0' ? '' : (params?.OrderType || ""),
- ChannelType: params?.PaymentMethod || "",
+ ChannelType: params?.CHANNEL_TYPE === '0' ? '' : params?.CHANNEL_TYPE || "",
StartDate: params?.ORDER_DATE_Start || "",
EndDate: params?.ORDER_DATE_End || "",
SearchKeyName: "MERCHANTS_NAME,COMMODITY_NAME,SALEBILL_CHILD_CODE,ORDER_PERSON,ORDER_PERSONTEL",
SearchKeyValue: params?.searchText || "",
SortStr: ""
}
+ console.log('reqreqreq222', req);
const data = await handeGetSalebillAccountList(req)
setSearchParams(params)
setCurrentSearchText(params?.searchText || "")
diff --git a/src/utils/exportExcelFun.ts b/src/utils/exportExcelFun.ts
index 8521675..224c191 100644
--- a/src/utils/exportExcelFun.ts
+++ b/src/utils/exportExcelFun.ts
@@ -29,15 +29,33 @@ function flattenTree>(
return out;
}
-/** 抽取 React 节点文本 */
+/** 抽取 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, '');
+ if (typeof node === 'string' || typeof node === 'number') {
+ // Excel 内部单元格换行的唯一标准是 \n
+ return String(node).replace(/
/gi, '\n').replace(/\r\n/g, '\n');
+ }
+
+ // 支持 React 的
节点
+ if (node?.type === 'br') return '\n';
+
+ const props = node?.props;
+ if (props) {
+ // 渲染 dangerouslySetInnerHTML
+ const html = props?.dangerouslySetInnerHTML?.__html;
+ if (typeof html === 'string') {
+ return html
+ .replace(/
/gi, '\n')
+ .replace(/<[^>]+>/g, '')
+ .replace(/ /g, ' ');
+ }
+ // 递归渲染 children
+ const children = props?.children;
+ if (Array.isArray(children)) return children.map(extractText).join('');
+ if (children != null) return extractText(children);
+ }
+
return String(node ?? '');
}
@@ -143,7 +161,7 @@ function getCellValue(col: AnyCol, record: any, rowIndex: number) {
/** 估算列宽(简单) */
const estimateWidth = (v: any) => {
const s = (v ?? '').toString();
- const len = Array.from(s).reduce((n, ch) => n + (/[^\x00-\xff]/.test(ch) ? 2 : 1), 0);
+ const len = Array.from(s).reduce((n: number, ch: any) => n + (/[^\x00-\xff]/.test(ch) ? 2 : 1), 0);
return Math.min(Math.max(len + 2, 8), 60);
};
@@ -196,14 +214,28 @@ export async function exportXlsxFromProColumnsExcelJS(
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;
+ const titleContent = extractText(topTitle);
+
+ // 重要:先执行合并,再分发数据和样式
+ ws.mergeCells(currentRowIndex, 1, currentRowIndex, columnCount || 1);
+
+ // 对合并后的 Master Cell 进行赋值和核心样式设置
+ const masterCell = ws.getCell(currentRowIndex, 1);
+ masterCell.value = titleContent;
+ masterCell.font = { bold: true, size: 14, name: 'Microsoft YaHei' };
+
+ // 重要:由于合并单元格会共享样式,直接对 Row 应用对齐是最稳妥的
+ row.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
+
+ // 双重加固:对合并范围内的所有单元格显式设置 wrapText (解决某些版本只看 Master Cell 却失效的问题)
+ for (let i = 1; i <= (columnCount || 1); i++) {
+ row.getCell(i).alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
+ }
+
+ // 动态行高计算(关键:确保换行后的内容不被遮盖)
+ const lines = titleContent.split('\n').length;
+ row.height = Math.max(lines * 32, 40);
+
currentRowIndex += 1;
}
@@ -231,17 +263,23 @@ export async function exportXlsxFromProColumnsExcelJS(
currentRowIndex += 1;
}
- // 3) 多级表头(全部居中 + 加粗)
+ // 3) 多级表头(全部居中 + 加粗 + 换行支持)
const headerStartRow = currentRowIndex;
headerAOA.forEach((r, idx) => {
const row = ws.getRow(headerStartRow + idx);
+ let maxRowLines = 1;
r.forEach((v, cIdx) => {
+ const text = extractText(v);
const cell = row.getCell(cIdx + 1);
- cell.value = v;
- cell.alignment = { horizontal: 'center', vertical: 'middle' };
+ cell.value = text;
+ cell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
cell.font = { bold: true };
+
+ const lines = text.split('\n').length;
+ if (lines > maxRowLines) maxRowLines = lines;
});
- row.height = 18;
+ row.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
+ row.height = Math.max(22, maxRowLines * 20);
});
// 应用表头合并
merges.forEach(m => {
@@ -257,24 +295,35 @@ export async function exportXlsxFromProColumnsExcelJS(
for (let i = 0; i < batch.length; i++) {
const rec = batch[i];
const row = ws.getRow(currentRowIndex + i);
+ let maxRowLines = 1;
leafCols.forEach((col, j) => {
- // 这里 rowIndex 仍然传全局行号 start + i,序号列会自动正确
- row.getCell(j + 1).value = getCellValue(col, rec, start + i);
+ const val = getCellValue(col, rec, start + i);
+ const text = extractText(val);
+ const cell = row.getCell(j + 1);
+ cell.value = text;
+ cell.alignment = { vertical: 'middle', wrapText: true };
+
+ const lines = text.split('\n').length;
+ if (lines > maxRowLines) maxRowLines = lines;
});
- // 适度让出主线程:ExcelJS 是纯 JS,通常也很稳;如需进一步优化可用 setTimeout 分批
+ row.height = Math.max(18, maxRowLines * 16);
}
currentRowIndex += batch.length;
- // 5) 列宽:基于表头 + 采样数据估算
+ // 5) 列宽与默认样式
const sampleRows = Math.min(batch.length, 200);
for (let c = 1; c <= columnCount; c++) {
+ const wsCol = ws.getColumn(c);
+ // 默认开启换行
+ wsCol.alignment = { vertical: 'middle', wrapText: true };
+
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);
+ wsCol.width = Math.max(headerMax, dataMax);
}
// 6) 冻结窗格:冻结到(标题 + 信息行 + 表头)这一行的下一行
@@ -290,7 +339,9 @@ export async function exportXlsxFromProColumnsExcelJS(
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
- a.download = `${filename}.xlsx`;
+ // 清理文件名中的换行符,防止下载异常
+ const safeFilename = (filename || '数据导出').replace(/[\r\n]/g, ' ');
+ a.download = `${safeFilename}.xlsx`;
a.click();
URL.revokeObjectURL(url);
}
\ No newline at end of file
diff --git a/src/utils/format.ts b/src/utils/format.ts
index 8e4048d..edd167c 100644
--- a/src/utils/format.ts
+++ b/src/utils/format.ts
@@ -923,15 +923,33 @@ function flattenTree>(
return out;
}
-/** 抽取 React 节点文本 */
+/** 抽取 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, '');
+ if (typeof node === 'string' || typeof node === 'number') {
+ // Excel 内部单元格换行的标准是 \n
+ return String(node).replace(/
/gi, '\n').replace(/\r\n/g, '\n');
+ }
+
+ // 支持 React 的
节点
+ if (node?.type === 'br') return '\n';
+
+ const props = node?.props;
+ if (props) {
+ // 渲染 dangerouslySetInnerHTML
+ const html = props?.dangerouslySetInnerHTML?.__html;
+ if (typeof html === 'string') {
+ return html
+ .replace(/
/gi, '\n')
+ .replace(/<[^>]+>/g, '')
+ .replace(/ /g, ' ');
+ }
+ // 递归渲染 children
+ const children = props?.children;
+ if (Array.isArray(children)) return children.map(extractText).join('');
+ if (children != null) return extractText(children);
+ }
+
return String(node ?? '');
}
/** 过滤 hideInTable(父级联动) */
@@ -1059,7 +1077,7 @@ function getCellValue(col: AnyCol, record: any, rowIndex: number) {
/** 估算列宽(简单) */
const estimateWidth = (v: any) => {
const s = (v ?? '').toString();
- const len = Array.from(s).reduce((n, ch) => n + (/[^\x00-\xff]/.test(ch) ? 2 : 1), 0);
+ const len = Array.from(s).reduce((n: number, ch: any) => n + (/[^\x00-\xff]/.test(ch) ? 2 : 1), 0);
return Math.min(Math.max(len + 2, 8), 60);
};
@@ -1126,12 +1144,27 @@ export async function exportXlsxFromProColumnsExcelJS(
// 顶部标题
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;
+ const titleContent = extractText(topTitle);
+
+ // 重要:先执行合并,再分发数据和样式
+ ws.mergeCells(currentRowIndex, 1, currentRowIndex, columnCount || 1);
+
+ // 对合并后的 Master Cell 进行赋值和核心样式设置
+ const masterCell = ws.getCell(currentRowIndex, 1);
+ masterCell.value = titleContent;
+ // 字体正常,不加粗
+ masterCell.font = { size: 14, name: 'Microsoft YaHei' };
+
+ // 强制对主单元格设置居中和换行
+ masterCell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
+
+ // 强制整行设置换行对齐属性作为辅助
+ row.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
+
+ // 计算行高 (基准 32 保证显示完整)
+ const lines = titleContent.split('\n').length;
+ row.height = Math.max(lines * 32, 40);
+
currentRowIndex += 1;
}
@@ -1157,17 +1190,23 @@ export async function exportXlsxFromProColumnsExcelJS(
currentRowIndex += 1;
}
- // 表头(保持原有样式与合并)
+ // 表头(多级支持)
const headerStartRow = currentRowIndex;
headerAOA.forEach((r, idx) => {
const row = ws.getRow(headerStartRow + idx);
+ let maxRowLines = 1;
r.forEach((v, cIdx) => {
+ const text = extractText(v);
const cell = row.getCell(cIdx + 1);
- cell.value = v;
- cell.alignment = { horizontal: 'center', vertical: 'middle' };
+ cell.value = text;
+ cell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
cell.font = { bold: true };
+
+ const lines = text.split('\n').length;
+ if (lines > maxRowLines) maxRowLines = lines;
});
- row.height = 18;
+ row.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
+ row.height = Math.max(22, maxRowLines * 18);
});
merges.forEach(m => {
ws.mergeCells(headerStartRow + (m.r1 - 1), m.c1, headerStartRow + (m.r2 - 1), m.c2);
@@ -1182,25 +1221,38 @@ export async function exportXlsxFromProColumnsExcelJS(
for (let i = 0; i < batch.length; i++) {
const rec = batch[i];
const row = ws.getRow(currentRowIndex + i);
+ let maxRowLines = 1;
leafCols.forEach((col, j) => {
const cell = row.getCell(j + 1);
- const v = getCellValue(col, rec, start + i);
- cell.value = v;
- if (col.align) cell.alignment = { horizontal: col.align, vertical: 'middle' };
+ const val = getCellValue(col, rec, start + i);
+ const text = extractText(val);
+ cell.value = text;
+
+ const alignment: any = { vertical: 'middle', wrapText: true };
+ if (col.align) alignment.horizontal = col.align;
+ cell.alignment = alignment;
+
+ const lines = text.split('\n').length;
+ if (lines > maxRowLines) maxRowLines = lines;
});
+ row.height = Math.max(18, maxRowLines * 16);
}
currentRowIndex += batch.length;
// 列宽估算
- const sampleRows = Math.min(batch.length, 200);
for (let c = 1; c <= columnCount; c++) {
+ const wsCol = ws.getColumn(c);
+ // 注意:不再此处设置 wsCol.alignment,防止覆盖标题和表头的水平居中样式
+ // 对齐已在数据行渲染阶段(Step 4)按列配置精准应用
+
const headerMax = Math.max(...headerAOA.map(r => estimateWidth(r[c - 1])));
let dataMax = 8;
- for (let i = 0; i < sampleRows; i++) {
+ const sampleRowsLimit = Math.min(batch.length, 100);
+ for (let i = 0; i < sampleRowsLimit; 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);
+ wsCol.width = Math.max(headerMax, dataMax);
}
// 注意:不再对 ws.views 进行任何设置(避免冻结表头)
diff --git a/src/versionEnv.ts b/src/versionEnv.ts
index ac8cc63..8f35c52 100644
--- a/src/versionEnv.ts
+++ b/src/versionEnv.ts
@@ -1,4 +1,4 @@
// 由 scripts/writeVersion.js 自动生成
-export const VERSION = "4.5.128";
-export const GIT_HASH = "772dc23";
-export const BUILD_TIME = "2026-01-23T11:34:36.109Z";
+export const VERSION = "4.5.131";
+export const GIT_HASH = "83cb30f";
+export const BUILD_TIME = "2026-01-26T10:28:52.569Z";