diff --git a/web/App_Code/api/activityController.cs b/web/App_Code/api/activityController.cs index 1fa0503..e051a34 100644 --- a/web/App_Code/api/activityController.cs +++ b/web/App_Code/api/activityController.cs @@ -1,4 +1,5 @@ using DocumentFormat.OpenXml.Drawing.Spreadsheet; +using MINOM.COM.Utility; using Model; using Newtonsoft.Json; using Org.BouncyCastle.Crypto; @@ -68,35 +69,43 @@ public class activityController : ApiController // DELETE api//5 public void Delete(int id) { - var prod = _db.activities.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料 - if (prod != null) + try { - //刪除相關資料 - var prod2 = _db.activity_relating.Where(q => q.activity_num == id).ToList(); //相關項目 - if (prod2.Count > 0) + var prod = _db.activities.Where(q => q.num == id).FirstOrDefault(); //刪除該筆資料 + if (prod != null) { - //查詢結果全部刪除 - _db.activity_relating.RemoveRange(prod2); - } - var prod3 = _db.activity_spares.Where(q => q.activity_num == id).ToList(); //備品項目 - if (prod3.Count > 0) - { - _db.activity_spares.RemoveRange(prod3); - } - - //訊息、庫存、收支,從資料庫設定null - - - _db.activities.Remove(prod); - _db.SaveChanges();//執行 - - Model.admin_log admin_log = new Model.admin_log(); - MyWeb.admin admin = new MyWeb.admin();//api裡不可以用MyWeb - if (admin.isLoign()) - { - admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Activity, (int)Model.admin_log.Status.Delete, prod.subject); + //刪除相關資料 + var prod2 = _db.activity_relating.Where(q => q.activity_num == id).ToList(); //相關項目 + if (prod2.Count > 0) + { + //查詢結果全部刪除 + _db.activity_relating.RemoveRange(prod2); + } + var prod3 = _db.activity_spares.Where(q => q.activity_num == id).ToList(); //備品項目 + if (prod3.Count > 0) + { + _db.activity_spares.RemoveRange(prod3); + } + + //訊息、庫存、收支,從資料庫設定null + + + _db.activities.Remove(prod); + _db.SaveChanges();//執行 + + Model.admin_log admin_log = new Model.admin_log(); + MyWeb.admin admin = new MyWeb.admin();//api裡不可以用MyWeb + if (admin.isLoign()) + { + admin_log.writeLog(admin.info.u_id, (int)Model.admin_log.Systems.Activity, (int)Model.admin_log.Status.Delete, prod.subject); + } } } + catch (Exception ex) + { + LogUtility log=new LogUtility(); + log.writeErrorPath(ex.ToString()+ex.StackTrace); + } } [HttpDelete] diff --git a/web/App_Code/api/ezAuthorizeAttribute.cs b/web/App_Code/api/ezAuthorizeAttribute.cs index 080c9f7..414ad3c 100644 --- a/web/App_Code/api/ezAuthorizeAttribute.cs +++ b/web/App_Code/api/ezAuthorizeAttribute.cs @@ -1,4 +1,5 @@ -using System; +using MINOM.COM.Utility; +using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -47,12 +48,21 @@ public class ezAuthorizeAttribute : AuthorizeAttribute protected override bool IsAuthorized(HttpActionContext actionContext) { - MyWeb.admin admin = new MyWeb.admin(); - bool isLogin = admin.isLoign(); - //if (admin.chkAdmIP && (admin.chkTwIP || admin.chkAdmIP_Enable)) { } - //判斷是否登入 - if (!isLogin) { - return false; + try + { + MyWeb.admin admin = new MyWeb.admin(); + bool isLogin = admin.isLoign(); + //if (admin.chkAdmIP && (admin.chkTwIP || admin.chkAdmIP_Enable)) { } + //判斷是否登入 + if (!isLogin) + { + return false; + } + } + catch (Exception ex) + { + LogUtility log = new LogUtility(); + log.writeErrorPath(ex.ToString()+ex.StackTrace); } /* if (actionContext == null) diff --git a/web/admin/print/TabletDesign.aspx b/web/admin/print/TabletDesign.aspx index f736c9b..a9f72c1 100644 --- a/web/admin/print/TabletDesign.aspx +++ b/web/admin/print/TabletDesign.aspx @@ -1365,6 +1365,7 @@ html, body, * { // fontSize = el.style.fontSize; // rowWidth = fontSize * 2; //} + fontSize = el.style.fontSize; // 1. 依據 textHeight 與 textSpan 計算一行(垂直列)最多能放幾個名字 let th = Number(el.textHeight) || 0; @@ -1380,31 +1381,77 @@ html, body, * { if (cols < 1) cols = 1; let colWidth = Math.floor(w / cols); - html = this.renderNameList(slice, el, colWidth); + html = this.renderNameList(slice, el, 0); } else if (el.id === 'alive') { //console.log("createEl:", slice, el) - 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); - fontSize = Math.floor(rowWidth / 2); - if (fontSize > el.style.fontSize) { - fontSize = el.style.fontSize; - rowWidth = fontSize * 2; - } - html = this.renderLiveList(slice, el, rowWidth); + //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); + //fontSize = Math.floor(rowWidth / 2); + //if (fontSize > el.style.fontSize) { + // fontSize = el.style.fontSize; + // rowWidth = fontSize * 2; + //} + let th = Number(el.textHeight) || 0; + let span = Number(el.textSpan) || 0; + let h = Number(el.height); + let w = Number(el.width); + + let namesPerCol = Math.floor((h + span) / (th + span)); + if (namesPerCol < 1) namesPerCol = 1; + + // 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + let cols = Math.ceil(slice.length / namesPerCol); + if (cols < 1) cols = 1; + let colWidth = Math.floor(w / cols); + html = this.renderLiveList(slice, el, colWidth); } else { - html = el.text; + let normal = [] + normal.push(el.text); + html = this.renderNormalWithSpace(normal,el) + //html = el.text; } //el.style.fontSize return $(`
`) .css({ - position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: fontSize + 'pt', 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); }, + renderNormalWithSpace(mid, d) { + console.log("renderNormal:", d) + let self = this; + let txt = $(`
`).css({ + "z-index": 9999, visibility: d.visibility, + width: d.width, height: d.height, + padding: "1px", + "writing-mode": "vertical rl", + display: "flex", + "flex-direction": "row", + "flex-wrap": "wrap", + margin: "auto", + //"letter-spacing": "0.1em", + "column-gap": `${d.textSpan}px`, + //"row-gap": "1px", + "align-items": "flex-start", + "justify-content": "start", + "font-size":`${d.style?d.style.fontSize:d.fontSize}pt` + }); + //console.log("吼優:",mid); + mid.forEach(x => { + let dd = $(`
${x}
`).css({ + "margin-bottom": "10px", + "margin-left": "5px", + "text-justify": "inter-character", + "white-space": "pre-line", + }); + $(txt).append(dd); + }); + return txt; + }, calcIndividualFontSize(name, el, maxW) { let s = Number(el.style.fontSize); let maxH = Number(el.textHeight); // 限制在單一名字預設的區塊高度內 @@ -1413,106 +1460,257 @@ html, body, * { while (s >= 12) { let s_px = s * 1.333; // 將 pt 轉換為 px + // 如果單行高度夠放,直接回傳當下大小 (不會過度縮小) - if (l * s_px <= maxH) { - return s; - } + //if (l * s_px <= maxH) { + // return s; + //} // 單行放不下,模擬多行折疊後的總寬度 let charsPerCol = Math.floor(maxH / s_px); if (charsPerCol < 1) charsPerCol = 1; let cols = Math.ceil(l / charsPerCol); - let neededWidth = cols * (s_px * 1.1); // 1.1 為緊密行高 + let neededWidth = (cols===1)?s_px:cols * (s_px * 1.1); // 1.1 為緊密行高 // 如果折疊後的總寬度不超過分配到的橫向欄寬,就是最佳字體 - if (neededWidth <= maxW) { + if (neededWidth <= maxW&&(cols>1||l*s_px<=maxH)) { return s; } s -= 1; // 否則繼續縮小 1pt } return 12; // 極限最小字體 }, - - countFontSize(name, el, baseFontSize) { - let s = Number(baseFontSize); + renderNameList(names, el, rowWidth) { let h = Number(el.height); let w = Number(el.width); - let l = Number(name.length); + let th = Number(el.textHeight) || h; + let gap = 5; // 對應 span 的 margin-left - // 逐步縮小字體 (最低限制到 4pt) - while (s >= 4) { - // ★ 關鍵修正:將 pt 轉換為實際的像素(px)高度 (1pt ≈ 1.333px) - let s_px = s * 1.333; + let s = Number(el.style.fontSize); + let sizes = []; - // 如果單行的高度夠放 - if (l * s_px <= h) { - // 如果原本的字體就放得下回傳 0,否則回傳縮小後的字體大小 - return s === Number(baseFontSize) ? 0 : s; + //// 1. 模擬排版:找出能讓全體塞入的最佳字體組合 + //while (s >= 12) { + // let short_s_px = s * 1.333; + // let long_s = Math.floor(s * 0.75); // 長字體最多縮小 25% + // if (long_s < 12) long_s = 12; + // let long_s_px = long_s * 1.333; + + // let currentColHeight = 0; + // let totalWidth = 0; + // let tempSizes = []; + + // for (let i = 0; i < names.length; i++) { + // let name = names[i]; + // let l = name.length; + // let isMulti = (l * short_s_px) > th; + + // let size = isMulti ? long_s : s; + // let size_px = isMulti ? long_s_px : short_s_px; + + // tempSizes.push({ name: name, size: size, isMulti: isMulti }); + + // if (isMulti) { + // // 多行長名 + // let charsPerCol = Math.floor(h / size_px); + // if (charsPerCol < 1) charsPerCol = 1; + // let lines = Math.ceil(l / charsPerCol); + // let nameW = lines * (size_px * 1.1); // 1.1倍行距 + + // if (currentColHeight > 0) currentColHeight = 0; + // totalWidth += nameW + gap; + // } else { + // // 單行短名 + // if (currentColHeight > 0 && (currentColHeight + th + gap) <= h) { + // currentColHeight += th + gap; + // } else { + // currentColHeight = th; + // totalWidth += short_s_px + gap; + // } + // } + // } + + // if (totalWidth > 0) totalWidth -= gap; + + // // 總寬度未爆框,確定最佳組合 + // if (totalWidth <= w) { + // sizes = tempSizes; + // break; + // } + // s -= 1; + //} + + //// 處理極端情況 + //if (sizes.length === 0) { + // sizes = names.map(name => { + // return { name: name, size: 12, isMulti: (name.length * 12 * 1.333) > th }; + // }); + //} + // 1. 模擬排版:找出能讓全體塞入的最佳字體組合 + while (s >= 12) { + let short_s_px = s * 1.333; + let hasShort = names.some(n => (n.length * short_s_px) <= th); + + let long_s = hasShort ? Math.floor(s * 0.75) : s; // 長字體最多縮小 25% + if (long_s < 12) long_s = 12; + let long_s_px = long_s * 1.333; + + let currentColHeight = 0; + let totalWidth = 0; + let tempSizes = []; + let anyWrapped = false; + for (let i = 0; i < names.length; i++) { + let name = names[i]; + let l = name.length; + //let isMulti = (l * short_s_px) > th; + let isShort = (l * short_s_px) <= th; + let size = isShort ? s : long_s;//isMulti ? long_s : s; + let size_px = size * 1.333; //isMulti ? long_s_px : short_s_px; + + let isMulti = !isShort && (l * size_px >= h); + if (isMulti) anyWrapped = true; + + tempSizes.push({ name: name, size: size, isMulti: isMulti, isShort: isShort }); + + if (isMulti) { + // 多行長名 + let charsPerCol = Math.floor(h / size_px); + if (charsPerCol < 1) charsPerCol = 1; + let lines = Math.ceil(l / charsPerCol); + let nameW = lines * (size_px * 1.1); // 1.1倍行距 + + if (currentColHeight > 0) currentColHeight = 0; + totalWidth += nameW + gap; + } else { + if (isShort) { + // 單行短名 + if (currentColHeight > 0 && (currentColHeight + th + gap) <= h) { + currentColHeight += th + gap; + } else { + currentColHeight = th; + totalWidth += short_s_px + gap; + } + } else { + currentColHeight = 0; + totalWidth += size_px + gap; + } + } } - // 單行放不下,必須折行 - let charsPerCol = Math.floor(h / s_px); // 一行(垂直)最多能塞幾個字 - if (charsPerCol < 1) charsPerCol = 1; + if (totalWidth > 0) totalWidth -= gap; - let cols = Math.ceil(l / charsPerCol); // 計算總共需要折成幾行 - let neededWidth = cols * (s_px * 1.2); // 估算折行後需要的總像素寬度 (1.2為行距緩衝) + // 總寬度未爆框,確定最佳組合 + if (totalWidth <= w) { + if (hasShort) { + sizes = tempSizes; + break; + } else { + if (!anyWrapped || s === 12) { + sizes = tempSizes; + break; + } + } - // 若折行後的總寬度小於等於容器寬度,代表此字體大小剛好能安全放下 - if (neededWidth <= w) { - return s; } - s -= 1; // 放不下則縮小 1pt 繼續嘗試 + s -= 1; } - return 8; // 極限最小字體 - }, - renderNameList(names, el,rowWidth) { + + // 處理極端情況 + if (sizes.length === 0) { + sizes = names.map(name => { + return { name: name, size: 12, isMulti: (name.length * 12 * 1.333) > th }; + }); + } + + // 2. 建立外層的 Flexbox 容器 let $namelist = $(`
`).css({ - "writing-mode": "vertical rl", + "writing-mode": "vertical-rl", display: "flex", "flex-direction": "row", "flex-wrap": "wrap", + "align-content": "center", + "align-items": "center", // 確保元素靠上對齊 margin: "auto", width: `${el.width}px`, height: `${el.height}px`, border: "0px solid #ccc", padding: "1px", - "font-family": el.fontFamily, - //"letter-spacing": `${el.textSpan}px`, "column-gap": `${el.textSpan}px`, - "align-items": "center", + "font-family": el.fontFamily }); - // - //"row-gap": "1px", + + // 3. 將資料物件傳給 renderNameSpan 產生子元素 let self = this; - names.forEach(n => { - $namelist.append(self.renderNameSpan(n, el, rowWidth)) - }) + sizes.forEach(item => { + $namelist.append(self.renderNameSpan(item, el)); + }); + return $namelist; + //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": el.fontFamily, + // //"letter-spacing": `${el.textSpan}px`, + // "column-gap": `${el.textSpan}px`, + // "align-items": "center", + //}); + //// + ////"row-gap": "1px", + //let self = this; + //names.forEach(n => { + // $namelist.append(self.renderNameSpan(n, el, rowWidth)) + //}) + //return $namelist; }, - renderNameSpan(name, el, colWidth) { - // 為「這個名字」獨立計算出專屬的最完美字體大小 - let indSize = this.calcIndividualFontSize(name, el, colWidth); - // 2. 判斷是否發生了折行 (名字長度 * 單字像素高度 > 分配到的最大高度) - let s_px = indSize * 1.333; - let isMultiLine = (name.length * s_px) > Number(el.textHeight); + renderNameSpan(item, el, colWidth) { + let th = Number(el.textHeight) || Number(el.height) + //let minH = item.isMulti ? `${d.height}px` : `${d.textHeight}px`; + let minH = item.isShort ? `${th}px` : `${el.height}px` + ; + let alignStyle = item.isMulti ? "start" : "justify"; - // 3. 根據是否折行動態決定對齊方式 - // 單行:分散對齊 (justify) / 多行:置中對齊 (center) 或是改為 靠上對齊 (start) - let alignStyle = isMultiLine ? "start" : "justify"; - - return $(`${name}`).css({ + return $(`${item.name}`).css({ display: "block", - "min-height": `${el.textHeight}px`, + "min-height": minH, // 強制多行長名撐滿高度,避免其他名字墊底 "max-height": `${el.height}px`, width: "auto", "line-height": "1.1", - "text-align": alignStyle, // ★ 動態套用對齊方式 - "text-align-last": alignStyle, // ★ 動態套用最後一行對齊方式 + "text-align": alignStyle, + "text-align-last": alignStyle, "margin-left": "5px", "text-justify": "inter-character", "word-break": "break-all" }); + //// 為「這個名字」獨立計算出專屬的最完美字體大小 + //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" + //}); //return $(`${name}`).css({ // display: "block", // "min-height": `${el.textHeight}px`, @@ -1551,19 +1749,42 @@ html, body, * { }) return $namelist; }, - renderLiveSpan(name, el, rowWidth) { - //console.log("renderLiveSpan:", name, el ); - return $(`${name}`).css({ + renderLiveSpan(name, el, colWidth) { + + // 為「這個名字」獨立計算出專屬的最完美字體大小 + 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: `${rowWidth}px`, - "text-align": "justify", - "text-align-last": "justify", - + width: "auto", + "line-height": "1.1", + "text-align": alignStyle, // ★ 動態套用對齊方式 + "text-align-last": alignStyle, // ★ 動態套用最後一行對齊方式 "margin-left": "5px", "text-justify": "inter-character", - });//"margin-bottom": `${el.textSpan}px`, + "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) { @@ -1704,6 +1925,7 @@ html, body, * { //} // 1. 依據 textHeight 與 textSpan 計算一行(垂直列)最多能放幾個名字 + let th = Number(el.textHeight) || 0; let span = Number(el.textSpan) || 0; let h = Number(el.height); @@ -1718,47 +1940,71 @@ html, body, * { let colWidth = Math.floor(w / cols); //console.log(el.height, el.textHeight, perRow, rows, rowWidth, fontSize) - html = this.renderNameList(slice, el, colWidth);//el.style.fontSize + html = this.renderNameList(slice, el, 0);//el.style.fontSize $(`.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 }).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 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); - let fontSize = Math.floor(rowWidth / 2); - if (fontSize > el.style.fontSize) { - fontSize = el.style.fontSize; - rowWidth = fontSize * 2; - } - html = this.renderLiveList(slice, el, rowWidth); - $(`.tablet-element[id="${this.activeId}"]`).css({ - position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: fontSize + 'pt', fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility - }).html(html); - } else if (this.activeId === "righttitle") { - $(`.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, - width: el.width, height: el.height - }).text(el.text); - } else if (this.activeId === "lefttitle") { - $(`.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, - width: el.width, height: el.height - }).text(el.text); - } else if (this.activeId === "address") { - $(`.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, - width: el.width, height: el.height - }).text(el.text); - } else { + //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); + //let fontSize = Math.floor(rowWidth / 2); + //if (fontSize > el.style.fontSize) { + // fontSize = el.style.fontSize; + // rowWidth = fontSize * 2; + //} + let th = Number(el.textHeight) || 0; + let span = Number(el.textSpan) || 0; + let h = Number(el.height); + let w = Number(el.width); + + let namesPerCol = Math.floor((h + span) / (th + span)); + if (namesPerCol < 1) namesPerCol = 1; + + // 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + let cols = Math.ceil(slice.length / namesPerCol); + if (cols < 1) cols = 1; + let colWidth = Math.floor(w / cols); + html = this.renderLiveList(slice, el, colWidth); $(`.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 - }).text(el.text); + }).html(html); + } else if (this.activeId === "righttitle") { + let normal = []; + normal.push(el.text) + 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, + width: el.width, height: el.height + }).html(html)//.text(el.text); + } else if (this.activeId === "lefttitle") { + let normal = []; + normal.push(el.text) + 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, + width: el.width, height: el.height + }).html(html)//.text(el.text); + } else if (this.activeId === "address") { + let normal = []; + normal.push(el.text) + 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, + width: el.width, height: el.height + }).html(html)//.text(el.text); + } else { + let normal = []; + normal.push(el.text) + 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 + }).html(html)//.text(el.text); } //this.render(); }, diff --git a/web/admin/print/html/tablet-1_new.svg b/web/admin/print/html/tablet-1_new.svg index 7c52425..28cb902 100644 --- a/web/admin/print/html/tablet-1_new.svg +++ b/web/admin/print/html/tablet-1_new.svg @@ -147,9 +147,9 @@ - - - - + + + + \ No newline at end of file diff --git a/web/admin/print/print_multi_new.aspx b/web/admin/print/print_multi_new.aspx index b1d72e4..d2d31a0 100644 --- a/web/admin/print/print_multi_new.aspx +++ b/web/admin/print/print_multi_new.aspx @@ -367,92 +367,263 @@ let ancestorFontSize = 16; details.forEach(d => { if (d.isActive != "hidden") { - let newFontSize = self.scaleFontSize(mid, d.fontSize, d.width); + let newFontSize =d.fontSize //self.scaleFontSize(mid, d.fontSize, d.width); let html = ""; let alive = ""; let yang = ""; let bi = ""; if (d.elementID === 'title1') { - let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); - let rows = Math.ceil(Number(mid.length) / Number(perRow));//總行數 - let rowWidth = Math.floor(Number(d.width) / rows); - newFontSize = Math.floor(rowWidth / 2); - if (newFontSize > d.fontSize) { - newFontSize = d.fontSize; - rowWidth = newFontSize * 2; + + let h = Number(d.height); + let w = Number(d.width); + let th = Number(d.textHeight) || h; + let gap = 5; // 對應 span 的 margin-left + + let s = Number(d.fontSize); + let sizes = []; + + // 1. 模擬排版:找出能讓全體塞入的最佳字體組合 + while (s >= 12) { + let short_s_px = s * 1.333; + let hasShort = mid.some(n => (n.length * short_s_px) <= th); + + let long_s = hasShort ? Math.floor(s * 0.75) : s; // 長字體最多縮小 25% + if (long_s < 12) long_s = 12; + let long_s_px = long_s * 1.333; + + let currentColHeight = 0; + let totalWidth = 0; + let tempSizes = []; + let anyWrapped = false; + for (let i = 0; i < mid.length; i++) { + let name = mid[i]; + let l = name.length; + //let isMulti = (l * short_s_px) > th; + let isShort = (l * short_s_px) <= th; + let size = isShort ? s : long_s;//isMulti ? long_s : s; + let size_px = size * 1.333; //isMulti ? long_s_px : short_s_px; + + let isMulti = !isShort && (l * size_px >= h); + if (isMulti) anyWrapped = true; + + tempSizes.push({ name: name, size: size, isMulti: isMulti,isShort:isShort }); + + if (isMulti) { + // 多行長名 + let charsPerCol = Math.floor(h / size_px); + if (charsPerCol < 1) charsPerCol = 1; + let lines = Math.ceil(l / charsPerCol); + let nameW = lines * (size_px * 1.1); // 1.1倍行距 + + if (currentColHeight > 0) currentColHeight = 0; + totalWidth += nameW + gap; + } else { + if (isShort) { + // 單行短名 + if (currentColHeight > 0 && (currentColHeight + th + gap) <= h) { + currentColHeight += th + gap; + } else { + currentColHeight = th; + totalWidth += short_s_px + gap; + } + } else { + currentColHeight = 0; + totalWidth += size_px + gap; + } + } + } + + if (totalWidth > 0) totalWidth -= gap; + + // 總寬度未爆框,確定最佳組合 + if (totalWidth <= w) { + if (hasShort) { + sizes = tempSizes; + break; + } else { + if (!anyWrapped || s === 12) { + sizes = tempSizes; + break; + } + } + + } + s -= 1; } + + // 處理極端情況 + if (sizes.length === 0) { + sizes = names.map(name => { + return { name: name, size: 12, isMulti: (name.length * 12 * 1.333) > th }; + }); + } + + // 2. 建立外層的 Flexbox 容器 let $namelist = $(`
`).css({ - "writing-mode": "vertical rl", + "writing-mode": "vertical-rl", display: "flex", "flex-direction": "row", "flex-wrap": "wrap", + "align-content": "center", + "align-items": "center", // 確保元素靠上對齊 margin: "auto", width: `${d.width}px`, height: `${d.height}px`, border: "0px solid #ccc", padding: "1px", - "font-family": style.fontFamily, - //"letter-spacing": "0.1em", "column-gap": `${d.textSpan}px`, - //"row-gap": "1px", - "align-items": "center", - "justify-content": "start", + "font-family":d.fontFamily }); + sizes.forEach(item => { + let th = Number(d.textHeight) || Number(d.height) + //let minH = item.isMulti ? `${d.height}px` : `${d.textHeight}px`; + let minH = item.isShort ? `${th}px` : `${d.height}px` + ; + let alignStyle = item.isMulti ? "start" : "justify"; - mid.forEach(z => { - let info = self.getTextInfo(z, newFontSize); - let spanWidth = mid.length > 1 ? newFontSize : d.width - let height = d.textHeight; - if (info.height >= d.textHeight) {//width - height = d.height; - } - let $span = $(`${z}`).css({ + let $span= $(`${item.name}`).css({ display: "block", - "min-height": `${height}px`, + "min-height": minH, // 強制多行長名撐滿高度,避免其他名字墊底 "max-height": `${d.height}px`, - "max-width": `${rowWidth}px`, - //width: `${newFontSize}px`, - "text-align": "justify", - "text-align-last": "justify", - //"margin-bottom": "20px", + width: "auto", + "line-height": "1.1", + "text-align": alignStyle, + "text-align-last": alignStyle, "margin-left": "5px", "text-justify": "inter-character", - "white-space": "pre-line", - //"justify-content":"center" + "word-break": "break-all" }); $namelist.append($span); }); + //let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); + //let rows = Math.ceil(Number(mid.length) / Number(perRow));//總行數 + //let rowWidth = Math.floor(Number(d.width) / rows); + //newFontSize = Math.floor(rowWidth / 2); + //if (newFontSize > d.fontSize) { + // newFontSize = d.fontSize; + // rowWidth = newFontSize * 2; + //} + //let th = Number(d.textHeight) || 0; + //let span = Number(d.textSpan) || 0; + //let h = Number(d.height); + //let w = Number(d.width); + + //let namesPerCol = Math.floor((h + span) / (th + span)); + //if (namesPerCol < 1) namesPerCol = 1; + + //// 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + //let cols = Math.ceil(mid.length / namesPerCol); + //if (cols < 1) cols = 1; + //let colWidth = Math.floor(w / cols); + //let $namelist = $(`
`).css({ + // "writing-mode": "vertical rl", + // display: "flex", + // "flex-direction": "row", + // "flex-wrap": "wrap", + // margin: "auto", + // width: `${d.width}px`, + // height: `${d.height}px`, + // border: "0px solid #ccc", + // padding: "1px", + // "font-family": style.fontFamily, + // //"letter-spacing": "0.1em", + // "column-gap": `${d.textSpan}px`, + // //"row-gap": "1px", + // "align-items": "center", + // "justify-content": "start", + //}); + + //mid.forEach(z => { + // //let info = self.getTextInfo(z, newFontSize); + // //let spanWidth = mid.length > 1 ? newFontSize : d.width + // //let height = d.textHeight; + // //if (info.height >= d.textHeight) {//width + // // height = d.height; + // //} + // //let $span = $(`${z}`).css({ + // // display: "block", + // // "min-height": `${height}px`, + // // "max-height": `${d.height}px`, + // // "max-width": `${rowWidth}px`, + // // //width: `${newFontSize}px`, + // // "text-align": "justify", + // // "text-align-last": "justify", + // // //"margin-bottom": "20px", + // // "margin-left": "5px", + // // "text-justify": "inter-character", + // // "white-space": "pre-line", + // // //"justify-content":"center" + // //}); + // let indSize = this.calcIndividualFontSize(z, d, colWidth); + // // 2. 判斷是否發生了折行 (名字長度 * 單字像素高度 > 分配到的最大高度) + // let s_px = indSize * 1.333; + // let isMultiLine = (z.length * s_px) > Number(d.textHeight); + + // // 3. 根據是否折行動態決定對齊方式 + // // 單行:分散對齊 (justify) / 多行:置中對齊 (center) 或是改為 靠上對齊 (start) + // let alignStyle = isMultiLine ? "start" : "justify"; + // let $span = $(`${z}`).css({ + // display: "block", + // "min-height": `${d.textHeight}px`, + // "max-height": `${d.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" + // }); + // $namelist.append($span); + //}); html = $namelist; } else if (d.elementID === "alive") { - let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); - let rows = Math.ceil(Number(left.length) / Number(perRow));//總行數 - let rowWidth = Math.floor(Number(d.width) / rows); - newFontSize = Math.floor(rowWidth / 2); - if (newFontSize > d.fontSize) { - newFontSize = d.fontSize; - rowWidth = newFontSize * 2; - } - console.log(d.height,d.width,d.textHeight,perRow,rows,rowWidth,newFontSize,d) - html = self.renderLiveList(left, d, rowWidth); + //let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); + //let rows = Math.ceil(Number(left.length) / Number(perRow));//總行數 + //let rowWidth = Math.floor(Number(d.width) / rows); + //newFontSize = Math.floor(rowWidth / 2); + //if (newFontSize > d.fontSize) { + // newFontSize = d.fontSize; + // rowWidth = newFontSize * 2; + //} + let th = Number(d.textHeight) || 0; + let span = Number(d.textSpan) || 0; + let h = Number(d.height); + let w = Number(d.width); + + let namesPerCol = Math.floor((h + span) / (th + span)); + if (namesPerCol < 1) namesPerCol = 1; + + // 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + let cols = Math.ceil(left.length / namesPerCol); + if (cols < 1) cols = 1; + let colWidth = Math.floor(w / cols); + + html = self.renderLiveList(left, d, colWidth); //newFontSize = d.fontSize; } else if (d.elementID === "titletriangle") { + newFontSize = self.scaleFontSize(mid, d.fontSize, d.width); html = self.renderRoster(mid, d) } else if (d.elementID === "combined") { ancestorFontSize = d.fontSize; + newFontSize = self.scaleFontSize(mid, d.fontSize, d.width); const parts = mid.join("\n").split('\n'); html = `
${parts[0] || ''} ${parts[1] || ''}
`; } else if (d.elementID === "address") { + newFontSize = self.scaleFontSize(mid, d.fontSize, d.width); html = self.renderNormal(address, d) } else if (d.elementID === "lefttitle") { + newFontSize = self.scaleFontSize(mid, d.fontSize, d.width); html = self.renderNormal(lefttitle, d) } else if (d.elementID === "righttitle") { + newFontSize = self.scaleFontSize(mid, d.fontSize, d.width); html = self.renderNormal(righttitle, d) } if (newFontSize < 10) { @@ -815,6 +986,35 @@ fix2(val) { return Number.parseFloat(val).toFixed(2); }, + calcIndividualFontSize(name, el, maxW) { + let s = Number(el.style ? el.style.fontSize : (el.fontSize ? el.fontSize : 18)); + let maxH = Number(el.textHeight); // 限制在單一名字預設的區塊高度內 + let l = Number(name.length); + + while (s >= 12) { + let s_px = s * 1.333; // 將 pt 轉換為 px + + + // 如果單行高度夠放,直接回傳當下大小 (不會過度縮小) + //if (l * s_px <= maxH) { + // return s; + //} + + // 單行放不下,模擬多行折疊後的總寬度 + let charsPerCol = Math.floor(maxH / s_px); + if (charsPerCol < 1) charsPerCol = 1; + + let cols = Math.ceil(l / charsPerCol); + let neededWidth = (cols === 1) ? s_px : cols * (s_px * 1.1); // 1.1 為緊密行高 + + // 如果折疊後的總寬度不超過分配到的橫向欄寬,就是最佳字體 + if (neededWidth <= maxW && (cols > 1 || l * s_px <= maxH)) { + return s; + } + s -= 1; // 否則繼續縮小 1pt + } + return 12; // 極限最小字體 + }, renderLiveList(names, el,rowWidth) { let $namelist = $(`
`).css({ "writing-mode": "vertical rl", @@ -838,19 +1038,41 @@ }) return $namelist; }, - renderLiveSpan(name, el,rowWidth) { - return $(`${name}`).css({ + renderLiveSpan(name, el, colWidth) { + // 為「這個名字」獨立計算出專屬的最完美字體大小 + 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: `${rowWidth}px`, - - "text-align": "justify", - "text-align-last": "justify", - "margin-bottom": "10px", + width: "auto", + "line-height": "1.1", + "text-align": alignStyle, // ★ 動態套用對齊方式 + "text-align-last": alignStyle, // ★ 動態套用最後一行對齊方式 + "margin-left": "5px", "text-justify": "inter-character", - "white-space": "pre-line" + "word-break": "break-all" }); + //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-bottom": "10px", + // "text-justify": "inter-character", + // "white-space": "pre-line" + //}); }, // 品字佈局邏輯:一上二下 renderRoster(names, el) { diff --git a/web/admin/print/tablet_edit/editorNewOne.html b/web/admin/print/tablet_edit/editorNewOne.html index 8f74017..b6cc538 100644 --- a/web/admin/print/tablet_edit/editorNewOne.html +++ b/web/admin/print/tablet_edit/editorNewOne.html @@ -368,13 +368,14 @@ if (response.status == 200) { self.printData = response.data; + console.log("GetDetailToPrint:",response); self.printData.forEach(x => { //console.log("GetDetailToPrint:",x.style); if (x.style != null && x.style != undefined) { self.selectStyle.style = x.style; self.selected.style = x.style; self.defaultStyle = x.style; - //$("#styleSel").val(x.style) + $("#styleSel").val(x.style) } if (x.f_num_tablet != "") { let tablet = JSON.parse(x.f_num_tablet); @@ -500,33 +501,43 @@ if (mid.length == 0) { mid.push("請選擇") } - let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); - let rows = Math.ceil(Number(mid.length) / Number(perRow));//總行數 - let rowWidth = Math.floor(Number(d.width) / rows); - newFontSize = Math.floor(rowWidth / 2); - if (newFontSize > d.fontSize) { - newFontSize = d.fontSize; - rowWidth = newFontSize * 2; - } + // 1. 依據 textHeight 與 textSpan 計算一行(垂直列)最多能放幾個名字 + + let th = Number(d.textHeight) || 0; + let span = Number(d.textSpan) || 0; + let h = Number(d.height); + let w = Number(d.width); + + let namesPerCol = Math.floor((h + span) / (th + span)); + if (namesPerCol < 1) namesPerCol = 1; + + // 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + let cols = Math.ceil(mid.length / namesPerCol); + if (cols < 1) cols = 1; + let colWidth = Math.floor(w / cols); //console.log(perRow, rows, rowWidth, newFontSize) - html = self.renderNameList(mid, d, newFontSize, style, rowWidth); + html = self.renderNameList(mid, d, newFontSize, style, colWidth); } else if (d.elementID === "alive") { self.tabletType = "B"; if (left.length == 0) { left.push("請選擇"); } - newFontSize = self.scaleFontSize(left, d.fontSize, d.Width); - let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); - let rows = Math.ceil(Number(left.length) / Number(perRow));//總行數 - let rowWidth = Math.floor(Number(d.width) / rows); - newFontSize = Math.floor(rowWidth / 2); - if (newFontSize > d.fontSize) { - newFontSize = d.fontSize; - rowWidth = newFontSize * 2; - } + newFontSize =d.fontSize //self.scaleFontSize(left, d.fontSize, d.Width); + let th = Number(d.textHeight) || 0; + let span = Number(d.textSpan) || 0; + let h = Number(d.height); + let w = Number(d.width); + + let namesPerCol = Math.floor((h + span) / (th + span)); + if (namesPerCol < 1) namesPerCol = 1; + + // 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + let cols = Math.ceil(left.length / namesPerCol); + if (cols < 1) cols = 1; + let colWidth = Math.floor(w / cols); //console.log(perRow, rows, rowWidth, newFontSize) //newFontSize = d.fontSize; - html = self.renderLiveList(left, d, rowWidth); + html = self.renderLiveList(left, d, colWidth); } else if (d.elementID === "titletriangle") { if (mid.length == 0) { @@ -1320,16 +1331,29 @@ Object.assign(left, self.alive); //this.printData.forEach(x => { let d = details.find(y => y.elementID == "alive"); - let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); - let rows = Math.ceil(Number(left.length) / Number(perRow));//總行數 - let rowWidth = Math.floor(Number(d.width) / rows); - let newFontSize = Math.floor(rowWidth / 2); - if (newFontSize > d.fontSize) { - newFontSize = d.fontSize; - rowWidth = newFontSize * 2; - } - self.selectedElement.css("font-size", newFontSize+"px"); - let html = self.renderLiveList(left, d); + //let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); + //let rows = Math.ceil(Number(left.length) / Number(perRow));//總行數 + //let rowWidth = Math.floor(Number(d.width) / rows); + //let newFontSize = Math.floor(rowWidth / 2); + //if (newFontSize > d.fontSize) { + // newFontSize = d.fontSize; + // rowWidth = newFontSize * 2; + //} + let th = Number(d.textHeight) || 0; + let span = Number(d.textSpan) || 0; + let h = Number(d.height); + let w = Number(d.width); + + let namesPerCol = Math.floor((h + span) / (th + span)); + if (namesPerCol < 1) namesPerCol = 1; + + // 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + let cols = Math.ceil(left.length / namesPerCol); + if (cols < 1) cols = 1; + let colWidth = Math.floor(w / cols); + self.selectedElement.css("font-size", d.fontSize + "px"); + console.log("B d:",d) + let html = self.renderLiveList(left, d,colWidth); $(self.selectedElement).append(html); } else if ($(self.selectedElement).children().first().hasClass("lefttitle") || $(self.selectedElement).children().first().hasClass("righttitle") || @@ -1365,21 +1389,24 @@ $(self.selectedElement).empty(); let d = details.find(y => y.elementID == "title1"); let newfontSize = self.scaleFontSize(mid, d.fontSize, d.textWidth) - let perRow = Math.floor(Number(d.height) / Number(d.textHeight)); - let rows = Math.ceil(Number(mid.length) / Number(perRow));//總行數 - let rowWidth = Math.floor(Number(d.width) / rows); - newfontSize = Math.floor(rowWidth / 2); - if (newfontSize > d.fontSize) { - newfontSize = d.fontSize; - rowWidth = newfontSize * 2; - } - if (newfontSize < 10) { - newfontSize = 10; - } + // 1. 依據 textHeight 與 textSpan 計算一行(垂直列)最多能放幾個名字 + + //let th = Number(d.textHeight) || 0; + //let span = Number(d.textSpan) || 0; + //let h = Number(d.height); + //let w = Number(d.width); + + //let namesPerCol = Math.floor((h + span) / (th + span)); + //if (namesPerCol < 1) namesPerCol = 1; + + //// 2. 計算總共會產生幾列 (cols),並推算出每個名字能分配到的最大欄寬 + //let cols = Math.ceil(mid.length / namesPerCol); + //if (cols < 1) cols = 1; + //let colWidth = Math.floor(w / cols); //console.log(self.selectedElement,perRow, rows, rowWidth, newfontSize) self.selectedElement.css("font-size", newfontSize+"px"); - let html = self.renderNameList(mid, d, style.fontSize, style,rowWidth); + let html = self.renderNameList(mid, d, style.fontSize, style,0); $(self.selectedElement).append(html); } else if ($(self.selectedElement).children().first().hasClass("rosterList")) { $(self.selectedElement).empty(); @@ -1404,6 +1431,35 @@ } } }, + calcIndividualFontSize(name, el, maxW) { + let s = Number(el.style ? el.style.fontSize : (el.fontSize ? el.fontSize : 18)); + let maxH = Number(el.textHeight); // 限制在單一名字預設的區塊高度內 + let l = Number(name.length); + + while (s >= 12) { + let s_px = s * 1.333; // 將 pt 轉換為 px + + + // 如果單行高度夠放,直接回傳當下大小 (不會過度縮小) + //if (l * s_px <= maxH) { + // return s; + //} + + // 單行放不下,模擬多行折疊後的總寬度 + let charsPerCol = Math.floor(maxH / s_px); + if (charsPerCol < 1) charsPerCol = 1; + + let cols = Math.ceil(l / charsPerCol); + let neededWidth = (cols === 1) ? s_px : cols * (s_px * 1.1); // 1.1 為緊密行高 + + // 如果折疊後的總寬度不超過分配到的橫向欄寬,就是最佳字體 + if (neededWidth <= maxW && (cols > 1 || l * s_px <= maxH)) { + return s; + } + s -= 1; // 否則繼續縮小 1pt + } + return 12; // 極限最小字體 + }, async itemAppend(e) { //console.log(e); }, @@ -1456,49 +1512,190 @@ }); return txt; }, - renderNameList(mid, d, newFontSize, style,rowWidth) { + renderNameList(names, d, newFontSize, style, colWidth) { let self = this; + let h = Number(d.height); + let w = Number(d.width); + let th = Number(d.textHeight) || h; + let gap = 5; // 對應 span 的 margin-left + + let s = Number(d.fontSize); + let sizes = []; + + // 1. 模擬排版:找出能讓全體塞入的最佳字體組合 + while (s >= 12) { + let short_s_px = s * 1.333; + let hasShort = names.some(n => (n.length * short_s_px) <= th); + + let long_s = hasShort? Math.floor(s * 0.75):s; // 長字體最多縮小 25% + if (long_s < 12) long_s = 12; + let long_s_px = long_s * 1.333; + + let currentColHeight = 0; + let totalWidth = 0; + let tempSizes = []; + let anyWrapped = false; + for (let i = 0; i < names.length; i++) { + let name = names[i]; + let l = name.length; + //let isMulti = (l * short_s_px) > th; + let isShort = (l * short_s_px) <= th; + let size = isShort ? s : long_s;//isMulti ? long_s : s; + let size_px = size * 1.333; //isMulti ? long_s_px : short_s_px; + + let isMulti = !isShort && (l * size_px >= h); + if (isMulti) anyWrapped = true; + + tempSizes.push({ name: name, size: size, isMulti: isMulti, isShort: isShort }); + + if (isMulti) { + // 多行長名 + let charsPerCol = Math.floor(h / size_px); + if (charsPerCol < 1) charsPerCol = 1; + let lines = Math.ceil(l / charsPerCol); + let nameW = lines * (size_px * 1.1); // 1.1倍行距 + + if (currentColHeight > 0) currentColHeight = 0; + totalWidth += nameW + gap; + } else { + if (isShort) { + // 單行短名 + if (currentColHeight > 0 && (currentColHeight + th + gap) <= h) { + currentColHeight += th + gap; + } else { + currentColHeight = th; + totalWidth += short_s_px + gap; + } + } else { + currentColHeight = 0; + totalWidth += size_px + gap; + } + } + } + + if (totalWidth > 0) totalWidth -= gap; + + // 總寬度未爆框,確定最佳組合 + if (totalWidth <= w) { + if (hasShort) { + sizes = tempSizes; + break; + } else { + if (!anyWrapped || s === 12) { + sizes = tempSizes; + break; + } + } + + } + s -= 1; + } + + // 處理極端情況 + if (sizes.length === 0) { + sizes = names.map(name => { + return { name: name, size: 12, isMulti: (name.length * 12 * 1.333) > th }; + }); + } let $namelist = $(`
`).css({ - "writing-mode": "vertical rl", + "writing-mode": "vertical-rl", display: "flex", "flex-direction": "row", "flex-wrap": "wrap", + "align-content": "center", + "align-items": "center", // 確保元素靠上對齊 margin: "auto", width: `${d.width}px`, height: `${d.height}px`, border: "0px solid #ccc", padding: "1px", - "font-family": style.fontFamily, - //"letter-spacing": "0.1em", "column-gap": `${d.textSpan}px`, - //"row-gap": "1px", - "align-items": "center", - "justify-content": "start", + "font-family": d.fontFamily }); - mid.forEach(z => { - let info = self.getTextInfo(z, newFontSize); - let spanWidth = mid.length > 1 ? newFontSize : d.width - let height = d.textHeight; - if (info.width >= d.textHeight) { - height = d.height; - } - let $span = $(`${z}`).css({ + sizes.forEach(item => { + let th = Number(d.textHeight) || Number(d.height) + //let minH = item.isMulti ? `${d.height}px` : `${d.textHeight}px`; + let minH = item.isShort ? `${th}px` : `${d.height}px` + ; + let alignStyle = item.isMulti ? "start" : "justify"; + + let $span= $(`${item.name}`).css({ display: "block", - "min-height": `${height}px`, + "min-height": minH, // 強制多行長名撐滿高度,避免其他名字墊底 "max-height": `${d.height}px`, - "max-width": `${rowWidth}px`, - //width: `${newFontSize}px`, - "text-align": "justify", - "text-align-last": "justify", - //"margin-bottom": "20px", + width: "auto", + "line-height": "1.1", + "text-align": alignStyle, + "text-align-last": alignStyle, "margin-left": "5px", "text-justify": "inter-character", - "white-space": "pre-line", - //"justify-content":"center" + "word-break": "break-all" }); $namelist.append($span); - }); + }) + //let $namelist = $(`
`).css({ + // "writing-mode": "vertical rl", + // display: "flex", + // "flex-direction": "row", + // "flex-wrap": "wrap", + // margin: "auto", + // width: `${d.width}px`, + // height: `${d.height}px`, + // border: "0px solid #ccc", + // padding: "1px", + // "font-family": style.fontFamily, + // //"letter-spacing": "0.1em", + // "column-gap": `${d.textSpan}px`, + // //"row-gap": "1px", + // "align-items": "center", + // "justify-content": "start", + //}); + + //mid.forEach(z => { + // //let info = self.getTextInfo(z, newFontSize); + // //let spanWidth = mid.length > 1 ? newFontSize : d.width + // //let height = d.textHeight; + // //if (info.width >= d.textHeight) { + // // height = d.height; + // //} + // //let $span = $(`${z}`).css({ + // // display: "block", + // // "min-height": `${height}px`, + // // "max-height": `${d.height}px`, + // // "max-width": `${rowWidth}px`, + // // //width: `${newFontSize}px`, + // // "text-align": "justify", + // // "text-align-last": "justify", + // // //"margin-bottom": "20px", + // // "margin-left": "5px", + // // "text-justify": "inter-character", + // // "white-space": "pre-line", + // // //"justify-content":"center" + // //}); + // // 為「這個名字」獨立計算出專屬的最完美字體大小 + // let indSize = this.calcIndividualFontSize(z, d, colWidth); + // // 2. 判斷是否發生了折行 (名字長度 * 單字像素高度 > 分配到的最大高度) + // let s_px = indSize * 1.333; + // let isMultiLine = (z.length * s_px) > Number(d.textHeight); + + // // 3. 根據是否折行動態決定對齊方式 + // // 單行:分散對齊 (justify) / 多行:置中對齊 (center) 或是改為 靠上對齊 (start) + // let alignStyle = isMultiLine ? "start" : "justify"; + // let $span = $(`${z}`).css({ + // display: "block", + // "min-height": `${d.textHeight}px`, + // "max-height": `${d.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" + // }); + // $namelist.append($span); + //}); return $namelist; }, renderLiveList(names, el,rowWidth) { @@ -1524,19 +1721,41 @@ }) return $namelist; }, - renderLiveSpan(name, el,rowWidth) { - return $(`${name}`).css({ + renderLiveSpan(name, el, colWidth) { + // 為「這個名字」獨立計算出專屬的最完美字體大小 + 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: `${rowWidth}px`, - height: `${el.textHeight}px`, - "text-align": "justify", - "text-align-last": "justify", - //"margin-bottom": "10px", + width: "auto", + "line-height": "1.1", + "text-align": alignStyle, // ★ 動態套用對齊方式 + "text-align-last": alignStyle, // ★ 動態套用最後一行對齊方式 + "margin-left": "5px", "text-justify": "inter-character", - "white-space": "pre-line" + "word-break": "break-all" }); + //return $(`${name}`).css({ + // display: "block", + // "min-height": `${el.textHeight}px`, + // "max-height": `${el.height}px`, + // width: `${rowWidth}px`, + // height: `${el.textHeight}px`, + // "text-align": "justify", + // "text-align-last": "justify", + // //"margin-bottom": "10px", + // "text-justify": "inter-character", + // "white-space": "pre-line" + //}); }, // 品字佈局邏輯:一上二下 renderRoster(names, el) {