From 53bf0b7fa931d24dae77ef8d8bd1f052c36eb056 Mon Sep 17 00:00:00 2001 From: kangwenjing <1138819403@qq.com> Date: Fri, 17 Apr 2026 16:31:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=87=BA=E5=AD=97=E6=AE=B5=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Expansion.tsx | 111 +++++++++++++++++++------------ 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/frontend/src/pages/Expansion.tsx b/frontend/src/pages/Expansion.tsx index a34a28bd..4c3a616c 100644 --- a/frontend/src/pages/Expansion.tsx +++ b/frontend/src/pages/Expansion.tsx @@ -309,12 +309,46 @@ function formatExportProjectCell(project?: { opportunityCode?: string; opportuni if (!project) { return ""; } - const lines = [ + const segments = [ normalizeExportText(project.opportunityCode) ? `编码:${normalizeExportText(project.opportunityCode)}` : "", normalizeExportText(project.opportunityName) ? `项目名称:${normalizeExportText(project.opportunityName)}` : "", project.amount === null || project.amount === undefined ? "" : `金额:${formatAmount(Number(project.amount))}`, ].filter(Boolean); - return lines.join("\n"); + return segments.join("|"); +} + +function formatExportProjectListCell(projects?: Array<{ opportunityCode?: string; opportunityName?: string; amount?: number | null }>) { + if (!projects?.length) { + return ""; + } + + return projects + .map((project, index) => { + const content = formatExportProjectCell(project); + return content ? `项目${index + 1} ${content}` : ""; + }) + .filter(Boolean) + .join("\n"); +} + +function getExcelDisplayWidth(value?: string | null) { + if (!value) { + return 0; + } + + return Array.from(value).reduce((total, char) => total + (/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef]/.test(char) ? 2 : 1), 0); +} + +function getExcelWrappedLineCount(value: string | null | undefined, columnWidth: number) { + if (!value) { + return 1; + } + + const safeWidth = Math.max(1, Math.floor(columnWidth)); + return value.split("\n").reduce((total, line) => { + const lineWidth = Math.max(1, getExcelDisplayWidth(line)); + return total + Math.max(1, Math.ceil(lineWidth / safeWidth)); + }, 0); } function formatExportFilenameTime(date = new Date()) { @@ -340,7 +374,6 @@ function downloadExcelFile(filename: string, content: BlobPart) { } function buildSalesExportHeaders(items: SalesExpansionItem[]) { - const maxProjects = Math.max(0, ...items.map((item) => item.relatedProjects?.length ?? 0)); const headers = [ "工号", "姓名", @@ -353,20 +386,15 @@ function buildSalesExportHeaders(items: SalesExpansionItem[]) { "销售是否在职", "销售以前是否做过云桌面项目", "跟进项目金额", + "项目信息", ]; - for (let index = 0; index < maxProjects; index += 1) { - headers.push(`项目${index + 1}`); - } - headers.push("创建人", "更新修改时间"); headers.push("跟进记录"); return headers; } function buildSalesExportData(items: SalesExpansionItem[]) { - const maxProjects = Math.max(0, ...items.map((item) => item.relatedProjects?.length ?? 0)); - return items.map((item) => { const row = [ normalizeExportText(item.employeeNo), @@ -380,13 +408,9 @@ function buildSalesExportData(items: SalesExpansionItem[]) { item.active === null || item.active === undefined ? "" : item.active ? "是" : "否", formatExportBoolean(item.hasExp), normalizeExportText(formatRelatedProjectAmount(item.relatedProjects)), + formatExportProjectListCell(item.relatedProjects), ]; - for (let index = 0; index < maxProjects; index += 1) { - const project = item.relatedProjects?.[index]; - row.push(formatExportProjectCell(project)); - } - row.push( normalizeExportText(item.owner), normalizeExportText(item.updatedAt), @@ -397,7 +421,6 @@ function buildSalesExportData(items: SalesExpansionItem[]) { } function buildChannelExportHeaders(items: ChannelExpansionItem[]) { - const maxProjects = Math.max(0, ...items.map((item) => item.relatedProjects?.length ?? 0)); const maxContacts = Math.max(0, ...items.map((item) => item.contacts?.length ?? 0)); const headers = [ "编码", @@ -415,12 +438,9 @@ function buildChannelExportHeaders(items: ChannelExpansionItem[]) { "人员规模", "以前是否做过云桌面项目", "跟进项目金额", + "项目信息", ]; - for (let index = 0; index < maxProjects; index += 1) { - headers.push(`项目${index + 1}`); - } - for (let index = 0; index < maxContacts; index += 1) { headers.push(`人员${index + 1}姓名`, `人员${index + 1}联系电话`, `人员${index + 1}职位`); } @@ -432,7 +452,6 @@ function buildChannelExportHeaders(items: ChannelExpansionItem[]) { } function buildChannelExportData(items: ChannelExpansionItem[]) { - const maxProjects = Math.max(0, ...items.map((item) => item.relatedProjects?.length ?? 0)); const maxContacts = Math.max(0, ...items.map((item) => item.contacts?.length ?? 0)); return items.map((item) => { @@ -452,13 +471,9 @@ function buildChannelExportData(items: ChannelExpansionItem[]) { item.size ? `${item.size}人` : "", formatExportBoolean(item.hasDesktopExp), normalizeExportText(formatRelatedProjectAmount(item.relatedProjects)), + formatExportProjectListCell(item.relatedProjects), ]; - for (let index = 0; index < maxProjects; index += 1) { - const project = item.relatedProjects?.[index]; - row.push(formatExportProjectCell(project)); - } - for (let index = 0; index < maxContacts; index += 1) { const contact = item.contacts?.[index]; row.push( @@ -1623,26 +1638,40 @@ export default function Expansion() { worksheet.addRow(headers); rows.forEach((row) => { - worksheet.addRow(row); + worksheet.addRow(row); }); worksheet.views = [{ state: "frozen", ySplit: 1 }]; - const followUpColumnIndex = headers.indexOf("跟进记录") + 1; - const projectColumnIndexes = headers - .map((header, index) => (header.startsWith("项目") ? index + 1 : -1)) - .filter((index) => index > 0); + const projectInfoColumnIndex = headers.indexOf("项目信息") + 1; worksheet.getRow(1).height = 24; worksheet.getRow(1).font = { bold: true }; worksheet.getRow(1).alignment = { vertical: "middle", horizontal: "center" }; + const projectInfoColumnWidth = projectInfoColumnIndex > 0 + ? Math.min( + 80, + Math.max( + 16, + getExcelDisplayWidth(headers[projectInfoColumnIndex - 1] || "") + 2, + rows.reduce((maxWidth, row) => { + const cellValue = row[projectInfoColumnIndex - 1]; + const longestLineWidth = typeof cellValue === "string" + ? cellValue.split("\n").reduce((lineMax, line) => Math.max(lineMax, getExcelDisplayWidth(line)), 0) + : 0; + return Math.max(maxWidth, longestLineWidth + 2); + }, 0), + ), + ) + : 30; + headers.forEach((header, index) => { const column = worksheet.getColumn(index + 1); if (header === "跟进记录") { column.width = 42; + } else if (header === "项目信息") { + column.width = projectInfoColumnWidth; } else if (header.includes("办公地址") || header.includes("备注")) { column.width = 24; - } else if (header.startsWith("项目")) { - column.width = 26; } else if (header.includes("渠道属性") || header.includes("内部属性") || header.includes("聚焦行业")) { column.width = 18; } else { @@ -1661,18 +1690,16 @@ export default function Expansion() { cell.alignment = { vertical: "top", horizontal: rowNumber === 1 ? "center" : "left", - wrapText: headers[columnNumber - 1] === "跟进记录" || headers[columnNumber - 1].startsWith("项目"), + wrapText: headers[columnNumber - 1] === "跟进记录" || headers[columnNumber - 1] === "项目信息", }; }); - if (rowNumber > 1 && followUpColumnIndex > 0) { - const followUpText = row.getCell(followUpColumnIndex).value as string | null | undefined; - const followUpLineCount = typeof followUpText === "string" && followUpText ? followUpText.split("\n").length : 1; - const projectLineCount = projectColumnIndexes.reduce((max, columnIndex) => { - const projectText = row.getCell(columnIndex).value as string | null | undefined; - const lineCount = typeof projectText === "string" && projectText ? projectText.split("\n").length : 1; - return Math.max(max, lineCount); - }, 1); - row.height = Math.max(22, Math.max(followUpLineCount, projectLineCount) * 16); + if (rowNumber > 1) { + const projectText = projectInfoColumnIndex > 0 ? row.getCell(projectInfoColumnIndex).value as string | null | undefined : ""; + const projectLineCount = getExcelWrappedLineCount( + typeof projectText === "string" ? projectText : "", + projectInfoColumnWidth, + ); + row.height = Math.max(22, projectLineCount * 16); } });