From 7f03559bfdc03f060e3b752cb760009380119eb3 Mon Sep 17 00:00:00 2001 From: ForeverSmiYng Date: Fri, 13 Mar 2026 18:35:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8A=A5=E8=A1=A8=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/template20260226001test010.xlsx | Bin 32181 -> 32172 bytes src/sql.ts | 147 +++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 10 deletions(-) diff --git a/public/template20260226001test010.xlsx b/public/template20260226001test010.xlsx index e986e96accf4fafb715eba898da7092f77e01018..9076364273c7fd1edbeaf52b9d410f5a2482a595 100644 GIT binary patch delta 3036 zcmYM$c{J4B9|!PRY?GZC+ek!GVa6_@Da(+hY}vAuB_w0t83`r(d@W@s+oQ2ggv{6? zOH!EZM#5z4u{HG+`g#1$`Tg!6_q^`se9pc1+&}JlH~j_^e}n74f$`>P-%URBzIQyq#Iy&^6aT|tR$@oH0r*heNU(&ABeK%%-i*9& zth}$gVbd(Jsnx>$_Opn(A=3Q88~+t8{I2_wHzmhvi>oiYY0ylR7ap_NBtuojapTcDhTWu|@DBvxlU90{_4-tnn;t}1+(XwgAwvm`nd_pP`D1XGp%C$BDeXF8eiq1q zeAKYOy*{TY0pF|ra3f!1^jlwcj_(*nFCa_U_JSiq~@YPSCLl_*Yux>~qgCOxi4)Iz^dp_f~?*7`>|mPXhw_8F3)F zLgCC(c|kaXl0sEm@?&(oQBEEUGWH19J4@o-W|C=}r8|}UWVvAkWr80)F&pTS=^_U8 zo|JVJO!9)Jvct+t$Ac=NAdoKr=9a+Q;%3x&qEYpK9xOxz%``w~^thNDoM$*yBQ%xx z?(>)EW|h@QbwodUVd9$^9W`YF zMtog!#qyM2ZUrpYjyY_(<|`9khH~OB%FH>r!H80rqntO3m|Ke-oTJpkJWa|0wQUj^ zg{&0Pw#mQ?-?yX-d+zM(UL01DE53=Ie%Q%3;i`j^AfeI-xoSdWb~EkexUs*be~Y?G zR$Z`A7`fUmG)V)CD^}R3yMP8%X2vcJUfGvs@%`R214lmWeyBG`|I~(OwRcP7Lj;-x z-=e_|PhHpxPNcU){mAj4m^V*(q0e$k3ndn;5KHkNw3dsVH6f+9d@=D(mWpG%LRm}H zd1RUp^DfhPR`iz-JgW%()|A@2-e&pHi{C%bud$jb8>LO~!t6FV%-1p&4lT-7ZOe$? zJ#*hLwCY_FQEcK3$*9Nssl}7*3_gl`oClCwzdJw2;%Ipb6`sCs-4^^xh{biL(R?Hs zS;uw`tiW9N<3%8a?MM2@*`ZmDXR?g~pwABwkWk?l)*FrPA7iKk`OfjqMJ^`N73roe zYkP`YD+^u%+y-Q0MSq09f41@o?}HTR23!fG-6##ci2_n=Qt*;@M{^_l*Wka2t1;A} zWZRM_@-9J7qCR~X7 zQTahtK>MAJpjwufH6L0+HU|96(#h^L+En`FNF_hlA6HD&?B#|cZF2L;VLen}?Bs9; zo}0cceCP6mwl~0MkGQB#DaCc3*QCh;Sd(kkA*p#?aQei+yk0r1AW6TjFhd!jt7R=Mc>PA{uc`ZNm|DuwY0H7in+0;(n_9+|>jJt6d4 z5m&lu<3Tg;Dy0hg3Wkz(T~_(2uSKY3G8H?Cg8`bjl?$+Jtvv zWj!7ASQ1~axuvGLK^ovYrw0o#8#L2m27!u!-_~mQ<`HDI#hEMhO3$YlFa$HNz?_vr zQXccCk-ixB15D>;&*9nNZ*HMGPG6s0lr{ZEmU((dF(ZP4rx&Jm{aNa@w8^zn(SoNi zxzdWQv&Z{w0yW{!*!nxPkx_fkWmomlieemCNiWQy1$FuCV2;zzpt*F6YANfIVb9a( zjqR^^7huiu#;Ih}7re#$V$_OQmPo3!=C79(EN{OXTB%fY7fRIqU>&hU!!yboz8b$_ z>+f+yEAhh)6rOAM^3&3-Z|;X1Y$FFN^AE7{!Fj9~qSH!#YB4U3+BL-5c8J$;5w9is z+%>44Jl~tQe2dgzNT4e>8GSM*a*R%n<@F))jK>?U9wmc?rRoFTPpyR&QdZvEw_0TD zd)X4m&evDnyNN{r)i~hK({kHhsGY}$C27T}gfQlsMOza3?UP*UWJklrKx%dVXibhN zR*qA&X3b%TC75ld$j8p9?s=+NQM`oN1GH#juqnPpNKfaYX3>FA?af zx-&Amc(bVK20F56m5yq6Eczw74Kb=sT4hv1iGe5Wb8@j*i86onoJI4m86b>K1LMnH zvB72Umy)uAp9wnoTAred7I$Qnn{ML^dM||!ycRK()iQWJ`DNu7cuM=wX^;KjQM9w; z{a1Wbtg(u+5a!IQ+ri3=oR$nRWxJ>|KDr!obLt5#n_QM1p$2^vL5$Prl`$Ot-J@#ojbW+Qam~NfE{}JG5ay&B^wrMH5jclwM2hMQT(}wjw6Xkq z-sn4QgY`;R;>crSk!jyo7lb%;pL(}d4_7yy?qZ#hXBf4qUC(|mv^=MW6ERGuc*;J{ ziyEMgieuMQ%pLm_JPRBNd#4>PE>_xwaDUh`k)-8pt|?Ad%wUC-|50bPx^A6K#IJ}g zF?C{g!Y<|a29PK)jSc+chQs`#l25sy!QptJh*45mq+#Q^d$`oXcRvJM z3S?i#{QA{3{a)35*pHVEHl)OAfK@?WLP@w@FgY1Z-2ApHO~Q@x97)=Qn3 z%00=lQ5dq@t*e)igSenVL!t)Y9&U4=PyWm`LO(3vabm$pq(xl5{V?!sdbvX#;^*_z zc+17K`CaU#{f|B%WPd=;WRxAA)|ks^z%nu$S^V+6&`_PIJ;+?X>axLPAtR@O?p zrgCiTDm5;~?wYR7+uLwO(%!(RXR6H4l8I=RK<8D-Xq@1mn3%3c5g+jK=c!7(KxP!K(nV|pF24t7KYP#=Ln{x9UQgFr(6-t1`n_j2pNLWCZqpYOO; sGZF=97dmF_5y>Zy*?+_U$zyUw9UUffOv@w2%O5j03c=D8^zWYk0Wq|y4gdfE delta 3039 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 diff --git a/src/sql.ts b/src/sql.ts index 2862a66..c6b6b9c 100644 --- a/src/sql.ts +++ b/src/sql.ts @@ -644,6 +644,7 @@ async function generateTemplate(data) { let allAddtional = {}; let allReserve = {}; let allMajors = {}; + let allServiceMajors = new Set(); data.scale?.forEach(sci => { allMajors[sci.major] = { [data.contracts.length]: sci }; }); @@ -999,6 +1000,7 @@ async function generateTemplate(data) { }; }); allDet.forEach((m, mindex) => { + allServiceMajors.add(m.major); let majorX = majorList[m.major]; cusInsertRowFunc(4 + num_2, [sheet_2.getRow(4)], sheet_2, (targetRow) => { targetRow.getCell(1).value = num_2++; @@ -1762,17 +1764,25 @@ async function generateTemplate(data) { // 更新编制说明 const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); - // ctx.font = "12pt 宋体"; - // const maxTitleWidth = 336; - // const maxTextWidth = 720; - const pageMaxHei = 733; + // const pageMaxHei = 733; // 20.25 25.5 33.75 const titleHeiDet = { fir: 11.25, els: 22.5 }; const textHeiDet = { fir: 6.75, els: 13.5 }; const descSheet = workbook.getWorksheet('编制说明'); - descSheet.getRow(1).getCell(1).value = data.name; - let descRowNum = 5; + let descRowNum = 1; + let titleArr = paragraphLineBreakFor1112(data.name, ctx, 0); + descSheet.getRow(descRowNum).getCell(1).value = titleArr[0]; + descRowNum++; + if (titleArr.length > 1) { + for (let i = 1; i < titleArr.length; i++) { + cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { + descRowNum++; + targetRow.getCell(1).value = titleArr[i]; + }); + } + } + descRowNum += 3; if (data.scale?.length) { let engCosts = data.scale.filter(f => f.major > 6 && majorList[f.major].hasCost); if (engCosts.length) { @@ -1827,8 +1837,18 @@ async function generateTemplate(data) { otherCosts.forEach(sci => { desc.push(`${majorList[sci.major].name}${Number(sci.cost).toLocaleString()}万元`); }); - descSheet.getRow(descRowNum).getCell(1).value = descSheet.getRow(descRowNum).getCell(1).value + `${desc.join(',')}。`; + let text = descSheet.getRow(descRowNum).getCell(1).value + `${desc.join(',')}。`; + let textArr = paragraphLineBreakFor1112(text, ctx); + descSheet.getRow(descRowNum).getCell(1).value = textArr[0]; descRowNum++; + if (textArr.length > 1) { + for (let i = 1; i < textArr.length; i++) { + cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { + descRowNum++; + targetRow.getCell(1).value = textArr[i]; + }); + } + } } else { descSheet.spliceRows(descRowNum, 1); } @@ -1836,6 +1856,38 @@ async function generateTemplate(data) { descSheet.spliceRows(descRowNum, 5); descRowNum++; } + descRowNum++; + if (allServiceMajors.size > 0) { + if (allServiceMajors.has(7) || allServiceMajors.has(18) || allServiceMajors.has(28)) { + descSheet.getRow(descRowNum).getCell(1).value = descSheet.getRow(descRowNum).getCell(1).value + '本次委托包括全部工程及费用。'; + descRowNum++; + } else { + let majorTexts = []; + let hasOther = false; + allServiceMajors.forEach(mid => { + if (mid > 6) { + majorTexts.push(majorList[mid].name); + } else { + hasOther = true; + } + }); + let text = descSheet.getRow(descRowNum).getCell(1).value + '本项目的' + majorTexts.join('、') + (hasOther ? '及其他专项工程。' : '。'); + let textArr = paragraphLineBreakFor1112(text, ctx); + descSheet.getRow(descRowNum).getCell(1).value = textArr[0]; + descRowNum++; + if (textArr.length > 1) { + for (let i = 1; i < textArr.length; i++) { + cusInsertRowFunc(descRowNum, [descSheet.getRow(descRowNum - 1)], descSheet, (targetRow) => { + descRowNum++; + targetRow.getCell(1).value = textArr[i]; + }); + } + } + } + } else { + descSheet.getRow(descRowNum).getCell(1).value = descSheet.getRow(descRowNum).getCell(1).value + '×××××。'; + } + return workbook; } catch (error) { console.log(error) @@ -1843,6 +1895,83 @@ async function generateTemplate(data) { } } +function paragraphLineBreakFor1112(paragraph, ctx, type = 1) {// 仅适用于内容为11或12号字体的段落 + // // number,最后判断是否数字 + // const numberNoBreak1 = /\d[\d,]*(?:\.\d+)?$/; + // const numberNoBreak2 = /^[\d,]*\.*\d+/; + // // number&unit + // const numberUnitNoBreak1 = /\d[\d,]*(?:\.\d+)?\s*(?:[a-zA-Z](?:[a-zA-Z\d·/]*[a-zA-Z\d])?)?$/; + // const numberUnitNoBreak2 = /^[\d,]*(?:\.\d+)?\s*[a-zA-Z](?:[a-zA-Z\d·/]*[a-zA-Z\d])?/; + // // 英文单词 + // const ewordNoBreak1 = /[a-zA-Z]+$/; + // const ewordNoBreak2 = /^[a-zA-Z]+/; + /* + 西文字符串 + */ + const matchASCIIChar1 = /[\x21-\x7E]+$/; + const matchASCIIChar2 = /^[\x21-\x7E]+/; + /* + 行首禁则 + 1.不能为段落第一个字符 + 2.将上一行最后1~2个字符转至下一行 + 3.上一行最后1~2个字符如符合数字、英文单词、公式等规则,应整个下移至本行 + */ + const forbidLineSta1 = /^[,,.。、::;;!!??)\]\})〕]}〉》」』】〗〙〛”’」』﹂﹄%‰℃°—–~·]/; + const forbidLineSta2 = /[,,.。、::;;!!??)\]\})〕]}〉》」』】〗〙〛”’」』﹂﹄%‰℃°—–~·]$/; + /* + 行尾禁则 + 1.不能为段落最后一个字符(即后续内容不能为空) + 2.将本行最后1~2个字符转至下一行 + */ + const forbidLineEnd = /[¥$€£@([{(〔[{〈《「『【〖〘〚“‘「『﹁﹃—–~·]+$/; + + ctx.font = "12pt 宋体"; + const maxWidth = type == 1 ? 720 : 336; + const minChar = Math.ceil(maxWidth / 8); + let text = paragraph; + let res = []; + // let enterRows = new Set(); + while (ctx.measureText(text).width > maxWidth) { + let t1 = text.slice(0, minChar); + let t1PX = ctx.measureText(t1).width; + while (t1PX > maxWidth) { + let t1MinChar = Math.floor((t1PX - maxWidth) / 16) || 1; + t1 = t1.slice(0, -1 * t1MinChar); + t1PX = ctx.measureText(t1).width; + } + let t2 = text.slice(t1.length); + if (matchASCIIChar1.test(t1) && matchASCIIChar2.test(t2)) { + let strASCII1 = t1.match(matchASCIIChar1)[0]; + if (strASCII1.length < t1.length) { + t1 = t1.slice(0, -1 * strASCII1.length); + t2 = text.slice(t1.length); + // enterRows.add(res.length); + } + } + if (forbidLineSta1.test(t2)) { + if (forbidLineSta2.test(t1)) { + t1 = t1.slice(0, -2); + } else { + t1 = t1.slice(0, -1); + } + t2 = text.slice(t1.length); + // enterRows.add(res.length); + } + if (forbidLineEnd.test(t1)) { + let endTrans = t1.match(forbidLineEnd)[0]; + t1 = t1.slice(0, -1 * (Math.min(endTrans.length, 2))); + t2 = text.slice(t1.length); + // enterRows.add(res.length); + } + res.push(t1); + text = text.slice(t1.length); + } + res.push(text); + // [...enterRows].forEach(ri => { + // res[ri] = res[ri] + '\n'; + // }); + return res; +} // 在指定位置插入行,并按模板行复制样式,可选回调填充值。 function cusInsertRowFunc(insertRowNum, sourceRows, worksheet, RowFun, cellFun) { @@ -1967,6 +2096,4 @@ function cloneCellValue(value) { return JSON.parse(JSON.stringify(value)); } return value; -} - - +} \ No newline at end of file