From 22e7c008ef57149619f8a6897263b3b85be3fbb2 Mon Sep 17 00:00:00 2001 From: minom Date: Mon, 7 Sep 2026 15:09:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=8E=92=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/admin/print/TabletDesign.aspx | 601 +++++++++++++++++++++------ web/admin/print/print_multi_new.aspx | 28 +- 2 files changed, 491 insertions(+), 138 deletions(-) diff --git a/web/admin/print/TabletDesign.aspx b/web/admin/print/TabletDesign.aspx index 624ba4d..436e839 100644 --- a/web/admin/print/TabletDesign.aspx +++ b/web/admin/print/TabletDesign.aspx @@ -610,7 +610,7 @@ html, body, * { text: "", }, tab: "型版", - value:"", + value: "", elements: [], activeId: null, styleID: null, @@ -641,7 +641,7 @@ html, body, * { newPaper: { paperName: "", paperWidth: "", - paperHeight:"" + paperHeight: "" }, styleName: "", showStyleMenu: false, @@ -655,7 +655,7 @@ html, body, * { removePaperID: "", paperMenuX: 0, paperMenuY: 0, - paperDialog:false, + paperDialog: false, } }, computed: { @@ -667,7 +667,7 @@ html, body, * { watch: { zoomScale: { handler() { - this.scaleSize =Math.floor( Number(this.zoomScale)*100) + this.scaleSize = Math.floor(Number(this.zoomScale) * 100) $(".tablet-paper").css({ transform: `scale(${this.zoomScale})` }); }, deep: true, @@ -675,7 +675,7 @@ html, body, * { }, methods: { minusZoomScalee() { - this.zoomScale = Number(this.zoomScale) -0.05 + this.zoomScale = Number(this.zoomScale) - 0.05 //this.scaleSize = this.scaleSize - 5 //$(".tablet-paper").css({ transform:`scale(${this.zoomScale})` }); }, @@ -706,7 +706,7 @@ html, body, * { return id === "origin" ? "原尺寸" : "合併"; } } - return "" + return "" }, init() { //$(".tablet-element").draggable({}); @@ -738,7 +738,7 @@ html, body, * { this.paperMenuX = e.clientX this.paperMenuY = e.clientY this.showPaperMenu = true - this.removePaperID=id + this.removePaperID = id }, closePaperMenu() { this.showPaperMenu = false; @@ -912,8 +912,8 @@ html, body, * { let size = this.allSize.find(x => x.name == paperSize) this.paper.width = this.orientation == "portrait" ? size.width : size.height; this.paper.height = this.orientation == "portrait" ? size.height : size.width; - - + + $(".tablet-paper").css({ "background-color": "white", width: this.paper.width + "mm", @@ -949,7 +949,7 @@ html, body, * { }, async getElements() { let self = this - self.tabletElements.length=0 + self.tabletElements.length = 0 await axios .post(HTTP_HOST + 'api/tablet/GetTabletElement', {}) .then(response => { @@ -968,7 +968,7 @@ html, body, * { async getStyles() { let self = this self.allStyle.length = 0 - // $(".style-menu").html("") + // $(".style-menu").html("") await axios .post(HTTP_HOST + 'api/tablet/GetStyleData', {}) .then(response => { @@ -1035,7 +1035,7 @@ html, body, * { this.printMode = s.printMode this.perpage = s.perpage this.styleID = id - this.styleName=s.name + this.styleName = s.name //this.rosterLimit = s.rosterLimit //$("#paperSize").val(s.paperSize); @@ -1103,12 +1103,12 @@ html, body, * { }); }, changeBg(item) { - this.backendInp=item.name + this.backendInp = item.name let path = this.bg.find(el => el.name == item.name)//$("#backendInp").val() $(".tablet-paper").css({ 'background-image': 'url(' + path.path + ')', 'background-size': '100% 100%' }) }, changePrintSize(id) { - this.printSize=id + this.printSize = id }, changePosition() { //this.activeId = id; @@ -1157,7 +1157,7 @@ html, body, * { return false } let ps = { paperSize: paperSize } - self.removeDialog=false + self.removeDialog = false await axios .post(HTTP_HOST + 'api/tablet/DelPaperSize', ps) .then(response => { @@ -1173,7 +1173,7 @@ html, body, * { } } else { this.snackbar.text = "存檔失敗"; - this.snackbar.show=true + this.snackbar.show = true } this.removeDialog = false; }); @@ -1201,7 +1201,7 @@ html, body, * { $("#toast_body").html(`${response.data.message}`) self.toast.show(); if (response.data.result == "Y") { - + self.getStyles() } } @@ -1233,12 +1233,12 @@ html, body, * { self.toast.show(); self.getPaperSize() //$("#paperSize").val("") - self.paperSize = self.allSize.length>0?self.allSize[0].id:"" + self.paperSize = self.allSize.length > 0 ? self.allSize[0].id : "" self.changePaper() self.paperDialog = false self.newPaper.paperName = "" self.newPaper.paperWidth = 0 - self.newPaper.paperHeight =0 + self.newPaper.paperHeight = 0 } } }); @@ -1314,7 +1314,7 @@ html, body, * { this.elements.forEach(el => { const names = el.text.split('\n').filter(s => s.trim()); const pages = Math.max(1, Math.ceil(names.length / self.rosterLimit)); - const slice = names.slice(0 * self.rosterLimit, (0 + 1) * self.rosterLimit); + const slice = names;//.slice(0 * self.rosterLimit, (0 + 1) * self.rosterLimit); $paper.append(self.createEl(el, slice)); }); //$canvas.append($paper); @@ -1390,13 +1390,13 @@ html, body, * { else { let normal = [] normal.push(el.text); - html = this.renderNormalWithSpace(normal,el) + html = this.renderNormalWithSpace(normal, el) //html = el.text; } //el.style.fontSize return $(`
`) .css({ - position: "absolute", left: el.x + "mm", top: el.y + "mm", fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility + position: "absolute", left: el.x + "mm", top: el.y + "mm", fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility //position: "absolute", left: el.startX + "mm", top: el.startY + "mm", fontSize: el.fontSize + 'pt', fontFamily: el.fontFamily, "z-index": 9999, visibility: el.isActive }) .html(html); @@ -1418,7 +1418,7 @@ html, body, * { //"row-gap": "1px", "align-items": "flex-start", "justify-content": "start", - "font-size":`${d.style?d.style.fontSize:d.fontSize}pt` + "font-size": `${d.style ? d.style.fontSize : d.fontSize}pt` }); //console.log("吼優:",mid); mid.forEach(x => { @@ -1451,17 +1451,17 @@ html, body, * { if (charsPerCol < 1) charsPerCol = 1; let cols = Math.ceil(l / charsPerCol); - let neededWidth = (cols===1)?s_px:cols * (s_px * 1.1); // 1.1 為緊密行高 + let neededWidth = (cols === 1) ? s_px : cols * (s_px * 1.1); // 1.1 為緊密行高 // 如果折疊後的總寬度不超過分配到的橫向欄寬,就是最佳字體 - if (neededWidth <= maxW&&(cols>1||l*s_px<=maxH)) { + if (neededWidth <= maxW && (cols > 1 || l * s_px <= maxH)) { return s; } s -= 1; // 否則繼續縮小 1pt } return 12; // 極限最小字體 }, - renderNameList(names, el, rowWidth) { + /*renderNameList(names, el, rowWidth) { let h = Number(el.height); let w = Number(el.width); let th = Number(el.textHeight) || h; @@ -1592,6 +1592,220 @@ html, body, * { // $namelist.append(self.renderNameSpan(n, el, rowWidth)) //}) //return $namelist; + },*/ + renderNameList(names, el) { + let h = Number(el.height); + let w = Number(el.width); + let th = Number(el.textHeight) || h; + let v_gap = Number(el.textSpan) || 0; + let h_gap = 5; + let shrinkLimit = 6; + + if (h < 500) { + shrinkLimit = 8; + } else if (h < 300) { + shrinkLimit = 12; + } + + let long_s = Number(el.style.fontSize); + let short_s = Number(el.style.fontSize); + + if (h < 500 && names.length == 1 && names[0].includes("暨無始劫以來累世冤親債主")) { + names = names[0].split("暨"); + names[0] = names[0] + "暨"; + } + + // 1. 初始判定與特例條件計算 + let items = names.map(name => { + let l = name.length; + let isShort = (l * 40) <= (th + (v_gap / 2)); + return { name: name, l: l, isShort: isShort, isMulti: false, size: 0 }; + }); + + let maxShortLen = Math.max(...items.filter(x => x.isShort).map(x => x.l), 1); + let allShort = items.every(x => x.isShort); + let fitsInHalf = items.every(x => (x.l * short_s * 1.333) <= (h / 2)); + + let isSpecial1 = allShort && fitsInHalf && items.length === 1; + let isSpecial2 = allShort && fitsInHalf && items.length === 2; + let isSpecial4 = allShort && fitsInHalf && (items.length === 4 || items.length === 6 || items.length === 8); + let isSpecial = isSpecial1 || isSpecial2 || isSpecial4; + + let sizes = []; + let shrink_turn = 'long'; + + // 2. 模擬排版與字體縮放運算 + while (true) { + let totalWidth = 0; + let current_col_h = 0; + let current_col_w = 0; + let cols_width = []; + let any_wrapped = false; + + for (let i = 0; i < items.length; i++) { + let item = items[i]; + let s = item.isShort ? short_s : long_s; + item.size = s; + let size_px = s * 1.333; + + if (item.isShort) { + let item_h = Math.max(th, maxShortLen * size_px); + if (isSpecial) { + if (isSpecial1 || isSpecial2) item_h = h / 2; + if (isSpecial4) item_h = (h / 2) - (v_gap / 2); + } else { + let natural_h = maxShortLen * size_px; + if (item_h > natural_h * 1.5) item_h = natural_h * 1.5; + } + + if (current_col_h === 0) { + current_col_h = item_h; + current_col_w = size_px; + } else if ((current_col_h + v_gap + item_h) <= h) { + current_col_h += (v_gap + item_h); + if (size_px > current_col_w) current_col_w = size_px; + } else { + cols_width.push(current_col_w); + current_col_h = item_h; + current_col_w = size_px; + } + } else { + if (current_col_h > 0) { + cols_width.push(current_col_w); + current_col_h = 0; + current_col_w = 0; + } + let item_w = size_px; + if ((item.l * size_px) > (h - 10)) { + item.isMulti = true; + any_wrapped = true; + let chars_per_col = Math.floor((h - 10) / size_px); + if (chars_per_col < 1) chars_per_col = 1; + let item_cols = Math.ceil(item.l / chars_per_col); + item_w = item_cols * (size_px * 1.1); + } else { + item.isMulti = false; + } + cols_width.push(item_w); + } + } + + if (current_col_h > 0) cols_width.push(current_col_w); + totalWidth = cols_width.reduce((a, b) => a + b, 0) + (cols_width.length > 1 ? (cols_width.length - 1) * h_gap : 0); + + if (totalWidth <= w && (!any_wrapped || long_s === shrinkLimit)) { + sizes = items.map(x => { + let final_s = x.size; + if (x.isShort) { + let max_s_for_th = Math.floor(th / (maxShortLen * 1.333)); + if (final_s > max_s_for_th) final_s = max_s_for_th; + } + return { name: x.name, size: final_s, isMulti: x.isMulti, isShort: x.isShort }; + }); + break; + } + + let hasLong = items.some(x => !x.isShort); + let hasShort = items.some(x => x.isShort); + if (hasLong && hasShort) { + if (shrink_turn === 'long') { + if (long_s > shrinkLimit) { long_s--; shrink_turn = 'short'; } + else if (short_s > shrinkLimit) { short_s--; shrink_turn = 'long'; } + else break; + } else { + if (short_s > shrinkLimit) { short_s--; shrink_turn = 'long'; } + else if (long_s > shrinkLimit) { long_s--; shrink_turn = 'short'; } + else break; + } + } else if (hasLong) { + if (long_s > shrinkLimit) long_s--; + else break; + } else { + if (short_s > shrinkLimit) short_s--; + else break; + } + } + + if (sizes.length === 0) { + sizes = names.map(name => ({ name: name, size: shrinkLimit, isMulti: false, isShort: true })); + } + + let flexDir = isSpecial2 ? "column" : "row"; + let flexJustify = isSpecial ? "center" : "flex-start"; + let flexAlign = "center"; + if (isSpecial4) v_gap = Number(h) / 4; + + let $namelist = $(`
`).css({ + "writing-mode": "vertical-rl", + display: "flex", + "flex-direction": flexDir, + "flex-wrap": "wrap", + "align-content": "center", + "align-items": flexAlign, + "justify-content": flexJustify, + margin: "auto", + width: `${w}px`, + height: `${h}px`, + border: "0px solid transparent", + padding: "1px", + "column-gap": `${v_gap}px`, + "font-family": el.style.fontFamily + }); + + sizes.forEach(item => { + let size_px = item.size * 1.333; + let natural_max_h = maxShortLen * size_px; + let boxH_num = item.isShort ? Math.max(th, natural_max_h) : h; + + if (isSpecial) { + if (isSpecial1 || isSpecial2) boxH_num = h / 2; + if (isSpecial4) boxH_num = (h / 2) - v_gap - 2; + } else if (item.isShort) { + if (boxH_num > natural_max_h * 1.5) boxH_num = natural_max_h * 1.5; + } + + let minH = `${boxH_num}px`; + let alignStyle = item.isMulti || (!item.isMulti && !item.isShort) ? "start" : "justify"; + let spanDisplay = "block"; + let spanJustifyContent = "flex-start"; + let spanMarginTop = "0px"; + + if (isSpecial1 || isSpecial2) spanMarginTop = `-${h / 4}px`; + if (isSpecial1 || isSpecial2 || isSpecial4) { + alignStyle = "justify"; + spanJustifyContent = "space-between"; + } + + let nameLen = item.name.length; + let contentH = nameLen * size_px; + let emptySpace = boxH_num - contentH; + let gapsCount = nameLen > 1 ? (nameLen - 1) : 1; + let avgGap = emptySpace / gapsCount; + + if (!isSpecial && alignStyle === "justify" && avgGap > (size_px * 1.5)) { + alignStyle = "justify"; + spanDisplay = "block"; + spanJustifyContent = "start"; + } + + let $span = $(`${item.name}`).css({ + "display": spanDisplay, + "justify-content": spanJustifyContent, + "align-items": "center", + "min-height": minH, + "max-height": `${h}px`, + "width": "auto", + "line-height": "1.1", + "text-align": alignStyle, + "text-align-last": alignStyle, + "margin-left": "5px", + "margin-top": spanMarginTop, + "text-justify": "inter-character", + "word-break": "break-all" + }); + $namelist.append($span); + }); + return $namelist; }, renderNameSpan(item, el, colWidth) { let th = Number(el.textHeight) || Number(el.height) @@ -1617,7 +1831,7 @@ html, body, * { //// 2. 判斷是否發生了折行 (名字長度 * 單字像素高度 > 分配到的最大高度) //let s_px = indSize * 1.333; //let isMultiLine = (name.length * s_px) > Number(el.textHeight); - + //// 3. 根據是否折行動態決定對齊方式 //// 單行:分散對齊 (justify) / 多行:置中對齊 (center) 或是改為 靠上對齊 (start) //let alignStyle = isMultiLine ? "start" : "justify"; @@ -1646,135 +1860,260 @@ html, body, * { // "text-justify": "inter-character", //});// "margin-bottom": `${el.textSpan}px`, }, + /* renderLiveList(names, el) { + //console.log("renderLiveList:", names); + //$("#imp-text").updateActive("text",names) + //let $namelist = $(`
`).css({ + // "writing-mode": "vertical rl", + // display: "flex", + // "flex-direction": "row", + // "flex-wrap": "wrap", + // margin: "auto", + // width: `${el.width}px`, + // height: `${el.height}px`, + // border: "0px solid #ccc", + // padding: "1px", + // "font-family": "BiauKai", + // //"letter-spacing": `${el.textSpan}px`, + // "column-gap": `${el.textSpan}px`, + // "align-items": "center", + //}); + //// "column-gap": `${el.textSpan}px`, + ////"row-gap": "1px", + //let self = this; + //names.forEach(n => { + // $namelist.append(self.renderLiveSpan(n, el, rowWidth)) + //}) + let h = Number(el.height); + let w = Number(el.width); + let th = Number(el.textHeight) || h; + + // 1. 單純計算總共有幾行 (列) + let namesPerCol = Math.floor(h / th); + if (namesPerCol < 1) namesPerCol = 1; + + let cols = Math.ceil(names.length / namesPerCol); // 總行數 + + // 2. 根據行數,縮小字體直到總寬度放得下 + let s = Number(el.style.fontSize); + while (s >= 4) { + let s_px = s * (4 / 3); // pt 轉 px + let lh = 1.1; // 行高 + let gap = 0; // 對應 span 的 margin-left + + // 總寬度 = 總行數 * (單行像素寬度 + 間距) + let totalWidth = cols * (s_px * lh + gap); + + // 放得下就停止縮小 + if (totalWidth <= w) { + break; + } + s -= 1; + } + + if (s < 4) s = 4; + + // 3. 建立外層 Flexbox 容器 + let $namelist = $(`
`).css({ + "writing-mode": "vertical-rl", + display: "flex", + "flex-direction": "row", + "flex-wrap": "wrap", + "align-content": "center", + "align-items": "center", + margin: "auto", + width: `${w}px`, + height: `${h}px`, + border: "0px solid #ccc", + padding: "0px", + "font-family": el.fontFamily, + "column-gap": `${el.textSpan}px`, + }); + + let self = this; + names.forEach(name => { + $namelist.append(self.renderLiveSpan(name, el, s)); + }); + return $namelist; + }, + renderLiveSpan(name, el, fontSize) { + let th = Number(el.textHeight) || Number(el.height); + + return $(`${name}`).css({ + display: "block", + "min-height": `${th}px`, // ★ 固定分配高度,交給 Flexbox 自動排版 + "max-height": `${el.height}px`, + width: "auto",//`${fontSize * 1.33}`, + //"line-height": "1.1", + "text-align": "justify", // 統一分散對齊 + "text-align-last": "justify", + //"margin-left": "5px", + "text-justify": "inter-character", + "word-break": "break-all", + "white-space": "pre-line" + }); + //// 為「這個名字」獨立計算出專屬的最完美字體大小 + //let indSize = this.calcIndividualFontSize(name, el, colWidth); + //// 2. 判斷是否發生了折行 (名字長度 * 單字像素高度 > 分配到的最大高度) + //let s_px = indSize * 1.333; + //let isMultiLine = (name.length * s_px) > Number(el.textHeight); + + //// 3. 根據是否折行動態決定對齊方式 + //// 單行:分散對齊 (justify) / 多行:置中對齊 (center) 或是改為 靠上對齊 (start) + //let alignStyle = isMultiLine ? "start" : "justify"; + + //return $(`${name}`).css({ + // display: "block", + // "min-height": `${el.textHeight}px`, + // "max-height": `${el.height}px`, + // width: "auto", + // "line-height": "1.1", + // "text-align": alignStyle, // ★ 動態套用對齊方式 + // "text-align-last": alignStyle, // ★ 動態套用最後一行對齊方式 + // "margin-left": "5px", + // "text-justify": "inter-character", + // "word-break": "break-all" + //}); + //console.log("renderLiveSpan:", name, el ); + //return $(`${name}`).css({ + // display: "block", + // "min-height": `${el.textHeight}px`, + // "max-height": `${el.height}px`, + // width: `${rowWidth}px`, + // "text-align": "justify", + // "text-align-last": "justify", + + // "margin-left": "5px", + // "text-justify": "inter-character", + //});//"margin-bottom": `${el.textSpan}px`, + },*/ renderLiveList(names, el) { - //console.log("renderLiveList:", names); - //$("#imp-text").updateActive("text",names) - //let $namelist = $(`
`).css({ - // "writing-mode": "vertical rl", - // display: "flex", - // "flex-direction": "row", - // "flex-wrap": "wrap", - // margin: "auto", - // width: `${el.width}px`, - // height: `${el.height}px`, - // border: "0px solid #ccc", - // padding: "1px", - // "font-family": "BiauKai", - // //"letter-spacing": `${el.textSpan}px`, - // "column-gap": `${el.textSpan}px`, - // "align-items": "center", - //}); - //// "column-gap": `${el.textSpan}px`, - ////"row-gap": "1px", - //let self = this; - //names.forEach(n => { - // $namelist.append(self.renderLiveSpan(n, el, rowWidth)) - //}) - let h = Number(el.height); - let w = Number(el.width); - let th = Number(el.textHeight) || h; + let h = parseFloat(el.height); + let w = parseFloat(el.width); + let th = parseFloat(el.textHeight) || h; + let v_gap = parseFloat(el.textSpan) || 0; + let h_gap = 5; + let shrinkLimit = 4; + if (Number(el.height) < 300) shrinkLimit = 8; + else if (Number(el.height) < 500) shrinkLimit = 6; - // 1. 單純計算總共有幾行 (列) - let namesPerCol = Math.floor(h / th); - if (namesPerCol < 1) namesPerCol = 1; + let s = parseFloat(el.style.fontSize); + if (names.length == 2) h = th + v_gap; - let cols = Math.ceil(names.length / namesPerCol); // 總行數 + let maxLen = Math.max(...names.map(x => x.length), 1); - // 2. 根據行數,縮小字體直到總寬度放得下 - let s = Number(el.style.fontSize); - while (s >= 4) { - let s_px = s * (4 / 3); // pt 轉 px - let lh = 1.1; // 行高 - let gap = 0; // 對應 span 的 margin-left + while (s >= shrinkLimit) { + let size_px = s * 1.333; + let current_col_h = 0; + let current_col_w = 0; + let cols_width = []; + let any_wrapped = false; - // 總寬度 = 總行數 * (單行像素寬度 + 間距) - let totalWidth = cols * (s_px * lh + gap); + for (let i = 0; i < names.length; i++) { + let l = names[i].length; + let natural_max_h = maxLen * size_px; + let item_h = Math.max(th, natural_max_h); + if (item_h > natural_max_h * 1.5) item_h = natural_max_h * 1.5; - // 放得下就停止縮小 - if (totalWidth <= w) { - break; + if (item_h > (h - 5)) { + any_wrapped = true; + if (current_col_h > 0) { + cols_width.push(current_col_w); + current_col_h = 0; + current_col_w = 0; + } + let chars_per_col = Math.floor((h - 5) / size_px); + if (chars_per_col < 1) chars_per_col = 1; + let item_cols = Math.ceil(l / chars_per_col); + cols_width.push(item_cols * (size_px * 1.1)); + } else { + if (current_col_h === 0) { + current_col_h = item_h; + current_col_w = size_px; + } else if (current_col_h + v_gap + item_h <= h) { + current_col_h += v_gap + item_h; + if (size_px > current_col_w) current_col_w = size_px; + } else { + cols_width.push(current_col_w); + current_col_h = item_h; + current_col_w = size_px; + } + } } + if (current_col_h > 0) cols_width.push(current_col_w); + let totalWidth = cols_width.reduce((a, b) => a + b, 0) + (cols_width.length > 1 ? (cols_width.length - 1) * h_gap : 0); + if (totalWidth <= w && (!any_wrapped || s === shrinkLimit)) break; s -= 1; } - if (s < 4) s = 4; + if (s < shrinkLimit) s = shrinkLimit; - // 3. 建立外層 Flexbox 容器 let $namelist = $(`
`).css({ "writing-mode": "vertical-rl", - display: "flex", - "flex-direction": "row", - "flex-wrap": "wrap", + "display": "flex", + "flex-flow": "wrap", "align-content": "center", "align-items": "center", - margin: "auto", - width: `${w}px`, - height: `${h}px`, - border: "0px solid #ccc", - padding: "0px", - "font-family": el.fontFamily, - "column-gap": `${el.textSpan}px`, + "margin": "auto", + "width": `${w}px`, + "height": `${h}px`, + "border": "0px solid transparent", + "padding": "0px", + "font-family": el.style.fontFamily, + "column-gap": `${v_gap}px` }); let self = this; names.forEach(name => { - $namelist.append(self.renderLiveSpan(name, el, s)); + $namelist.append(self.renderLiveSpan(name, el, s, maxLen)); }); return $namelist; }, - renderLiveSpan(name, el, fontSize) { - let th = Number(el.textHeight) || Number(el.height); + renderLiveSpan(name, el, fontSize, maxLen = 1) { + let h = parseFloat(el.height); + let th = parseFloat(el.textHeight) || h; + let size_px = fontSize * 1.333; + let l = name.length; + + let natural_max_h = maxLen * size_px; + let boxH_num = Math.max(th, natural_max_h); + if (boxH_num > natural_max_h * 1.5) boxH_num = natural_max_h * 1.5; + + let minH = `${boxH_num}px`; + let alignStyle = "justify"; + let spanDisplay = "block"; + let spanJustifyContent = "flex-start"; + + let contentH = l * size_px; + let emptySpace = boxH_num - contentH; + let gapsCount = l > 1 ? (l - 1) : 1; + let avgGap = emptySpace / gapsCount; + + if (alignStyle === "justify" && avgGap > (size_px * 1.5)) { + alignStyle = "justify"; + spanDisplay = "block"; + spanJustifyContent = "space-between"; + } return $(`${name}`).css({ - display: "block", - "min-height": `${th}px`, // ★ 固定分配高度,交給 Flexbox 自動排版 - "max-height": `${el.height}px`, - width: "auto",//`${fontSize * 1.33}`, - //"line-height": "1.1", - "text-align": "justify", // 統一分散對齊 - "text-align-last": "justify", - //"margin-left": "5px", + "display": spanDisplay, + "justify-content": spanJustifyContent, + "align-items": "center", + "min-height": minH, + "max-height": `${h}px`, + "width": "auto", + "text-align": alignStyle, + "text-align-last": alignStyle, + "margin-left": "0px", + "margin-top": "0px", "text-justify": "inter-character", "word-break": "break-all", "white-space": "pre-line" }); - //// 為「這個名字」獨立計算出專屬的最完美字體大小 - //let indSize = this.calcIndividualFontSize(name, el, colWidth); - //// 2. 判斷是否發生了折行 (名字長度 * 單字像素高度 > 分配到的最大高度) - //let s_px = indSize * 1.333; - //let isMultiLine = (name.length * s_px) > Number(el.textHeight); - - //// 3. 根據是否折行動態決定對齊方式 - //// 單行:分散對齊 (justify) / 多行:置中對齊 (center) 或是改為 靠上對齊 (start) - //let alignStyle = isMultiLine ? "start" : "justify"; - - //return $(`${name}`).css({ - // display: "block", - // "min-height": `${el.textHeight}px`, - // "max-height": `${el.height}px`, - // width: "auto", - // "line-height": "1.1", - // "text-align": alignStyle, // ★ 動態套用對齊方式 - // "text-align-last": alignStyle, // ★ 動態套用最後一行對齊方式 - // "margin-left": "5px", - // "text-justify": "inter-character", - // "word-break": "break-all" - //}); - //console.log("renderLiveSpan:", name, el ); - //return $(`${name}`).css({ - // display: "block", - // "min-height": `${el.textHeight}px`, - // "max-height": `${el.height}px`, - // width: `${rowWidth}px`, - // "text-align": "justify", - // "text-align-last": "justify", - - // "margin-left": "5px", - // "text-justify": "inter-character", - //});//"margin-bottom": `${el.textSpan}px`, }, + // 品字佈局邏輯:一上二下 - renderRoster(names, el,rowWidth) { + renderRoster(names, el, rowWidth) { if (!names.length) return ''; const mid = names.length === 1 ? 1 : Math.floor(names.length / 2); const top = names.slice(0, mid); @@ -1898,9 +2237,9 @@ html, body, * { }).html(html); } else if (this.activeId === "title1") { - + let names = el.text.split('\n').filter(s => s.trim()); - let slice = names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit); + let slice = names;//.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit); //let perRow = Math.floor(Number(el.height) / Number(el.textHeight)); //let rows = Math.ceil(Number(slice.length) / Number(perRow));//總行數 @@ -1933,7 +2272,7 @@ html, body, * { }).html(html); } else if (this.activeId === "alive") { let names = el.text.split('\n').filter(s => s.trim()); - let slice = names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit); + let slice = names;//.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit); //let perRow = Math.floor(Number(el.height) / Number(el.textHeight)); //let rows = Math.ceil(Number(slice.length) / Number(perRow));//總行數 //let rowWidth = Math.floor(Number(el.width) / rows); @@ -1961,7 +2300,7 @@ html, body, * { } else if (this.activeId === "righttitle") { let normal = []; normal.push(el.text) - html = this.renderNormalWithSpace(normal,el) + html = this.renderNormalWithSpace(normal, el) $(`.tablet-element[id="${this.activeId}"]`).css({ position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt', fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility, @@ -2100,7 +2439,7 @@ html, body, * { let master = { styleID: this.styleID, styleName: this.styleName, paperSize: this.paperSize,//$("#paperSize").val(), backendImg: this.backendInp, printSize: this.printSize, printMode: this.printMode, - orientation:"", printPageCount:this.perPage, rosterLimit: 8, + orientation: "", printPageCount: this.perPage, rosterLimit: 8, detail: detail } //console.log(master); diff --git a/web/admin/print/print_multi_new.aspx b/web/admin/print/print_multi_new.aspx index 20e75ea..cab7ce9 100644 --- a/web/admin/print/print_multi_new.aspx +++ b/web/admin/print/print_multi_new.aspx @@ -373,17 +373,20 @@ mid = mid[0].split("暨"); mid[0] = mid[0] + "暨"; } - + const init_fontSize = Number(d.fontSize) > 40 ? 40 : Number(d.fontSize); + //const init_size_px = init_fontSize * 1.333; // 1. 初始判定與特例條件計算 let items = mid.map(name => { let l = name.length; - let isShort = (l * 40) <= (th + (v_gap / 2)); + //應該用初始最大值而不是40 + let isShort = (l * init_fontSize) <= (th + (v_gap / 2)); return { name: name, l: l, isShort: isShort, isMulti: false, size: 0 }; }); let maxShortLen = Math.max(...items.filter(x => x.isShort).map(x => x.l), 1); let allShort = items.every(x => x.isShort); + console.log("全短名:",items,allShort); // 🚀 動態防呆:不寫死字數!用「實際字體大小」計算是否放得進半高。 // 這樣 13 個字會自動出局,而 3 個字的小牌位絕對能穩穩過關。 @@ -417,7 +420,7 @@ // 壓縮高度釋放空間 (支援反向放大字體) if (isSpecial) { if (isSpecial1 || isSpecial2) item_h = h / 2; - if (isSpecial4) item_h = (h / 2) - (v_gap / 2); + if (isSpecial4) item_h = (h / 2) - (v_gap )-2; } else { let natural_h = maxShortLen * size_px; if (item_h > natural_h * 1.5) { @@ -469,7 +472,18 @@ sizes = items.map(x => { let final_s = x.size; if (x.isShort) { - let max_s_for_th = Math.floor(th / (maxShortLen * 1.333)); + //let max_s_for_th = Math.floor(th / (maxShortLen * 1.333)); + //if (final_s > max_s_for_th) { + // final_s = max_s_for_th; + //} + // 🚀 關鍵修正:依照是否為特例,賦予實際可用的高度上限,解除原先 th 造成的字體壓抑 + let available_h = th; + if (isSpecial) { + if (isSpecial1 || isSpecial2) available_h = h / 2; + if (isSpecial4) available_h = (h / 2) - v_gap - 2; + } + let max_s_for_th = Math.floor(available_h / (maxShortLen * 1.333)); + if (final_s > max_s_for_th) { final_s = max_s_for_th; } @@ -511,9 +525,9 @@ let flexDir = isSpecial2 ? "column" : "row"; let flexJustify = isSpecial ? "center" : "flex-start"; let flexAlign = "center"; - if (isSpecial4) { - v_gap=Number(h)/4 - } + //if (isSpecial4) { + // v_gap=Number(h)/4 + //} let $namelist = $(`
`).css({ "writing-mode": "vertical-rl",