牌位排版調整

This commit is contained in:
2026-07-30 18:01:35 +08:00
parent cc85cb5bd7
commit 4b9448d548
6 changed files with 969 additions and 263 deletions
+293 -74
View File
@@ -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 = $(`<div class='nameList'></div>`).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 = $(`<span class="nameSpan">${z}</span>`).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= $(`<span style="font-size: ${item.size}pt;">${item.name}</span>`).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 = $(`<div class='nameList'></div>`).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 = $(`<span class="nameSpan">${z}</span>`).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 = $(`<span style="font-size: ${indSize}pt;">${z}</span>`).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 $(`<span class="liveSpan">${name}</span>`).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 $(`<span style="font-size: ${indSize}pt;">${name}</span>`).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 $(`<span class="liveSpan">${name}</span>`).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) {