This commit is contained in:
2026-03-11 17:37:27 +08:00
parent 5baac4fdb7
commit 0bb9da198b
2 changed files with 136 additions and 75 deletions

View File

@@ -232,7 +232,7 @@
elements: [], elements: [],
activeId: null, activeId: null,
styleID: null, styleID: null,
paper: { width: 100, height: 272 }, paper: { width: "100", height: "272" },
rosterLimit: 8, rosterLimit: 8,
allSize: [ allSize: [
//{ name: "A3", width: 297, height: 420, selected: "" }, //{ name: "A3", width: 297, height: 420, selected: "" },
@@ -301,6 +301,8 @@
}, },
print() { print() {
let s = this.allStyle.find(x => x.styleID == this.styleID)
let size = this.allSize.find(x=>x.id=s.paperSize)
let w = window.open('', '_blank'); let w = window.open('', '_blank');
if (!w) { if (!w) {
alert("請允許瀏覽器開啟彈出式視窗!"); alert("請允許瀏覽器開啟彈出式視窗!");
@@ -319,73 +321,114 @@
// 4. 組合列印視窗的 HTML // 4. 組合列印視窗的 HTML
let htmlContent = ` let htmlContent = `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>預覽列印 - 牌位設計</title> <title>預覽列印 - 牌位設計</title>
<meta charset="utf-8"> <meta charset="utf-8">
<style> <style>
/* =======================================
暴力防護層:避免外部 CSS 載入失敗或被干擾
======================================= */ /* 名單金字塔佈局容器 */
.roster-container {
width: 100%; height: 100%;
writing-mode: vertical-rl; /* 直書 */
display: flex;
/*flex-direction: column;*/ /* 雖然是直書,但物理上我們是將「上層區」和「下層區」垂直堆疊 */
flex-direction: row; /* 上下分層 (Top / Bottom) */
align-items: center; /* 左右置中對齊 */
justify-content: center;
gap: 20px; /* 這是「上層」跟「下層」之間的距離,可以設大一點 */
}
.roster-row {
display: flex;
flex-direction: row-reverse; /* 直書由右至左,所以 Row 要反向或依需求調整 */
justify-content: center;
gap: 8px; /* 名字之間的間距 */
margin-left: 10px; /* 上下排之間的間距 (因為是直書margin-left 是物理上的左邊/下方) */
}
.name-group {
display: flex;
flex-direction: column; /* ★★★ 關鍵:這讓名字左右並排 ★★★ */
justify-content: center;
align-items: center;
/* gap 由 HTML 動態綁定 */
}
.roster-name {
text-orientation: upright;
/*font-weight: bold;*/
white-space: nowrap;
line-height: 1.2;
font-family: 'Kaiti', serif;
/* 確保名字本身不會佔據過多寬度導致間距看起來很大 */
width: fit-content;
}
/* =======================================
暴力防護層:避免外部 CSS 載入失敗或被干擾
======================================= */
/* 【修復直書變橫書】強制寫入直書屬性 */ /* 【修復直書變橫書】強制寫入直書屬性 */
.vertical-text { .vertical-text {
writing-mode: vertical-rl !important; writing-mode: vertical-rl !important;
-webkit-writing-mode: vertical-rl !important; -webkit-writing-mode: vertical-rl !important;
text-orientation: mixed !important; text-orientation: mixed !important;
} }
/* 【修復底圖沒出現】強制印出背景設定 */ /* 【修復底圖沒出現】強制印出背景設定 */
* { * {
-webkit-print-color-adjust: exact !important; -webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important; print-color-adjust: exact !important;
color-adjust: exact !important; color-adjust: exact !important;
} }
/* 【修復排版沒照設定】覆寫原本 HTML 裡的 fixed並防止 Bootstrap 洗掉 absolute */ /* 【修復排版沒照設定】覆寫原本 HTML 裡的 fixed並防止 Bootstrap 洗掉 absolute */
.tablet-paper { .tablet-paper {
position: relative !important; position: relative !important;
margin: 0 auto !important; margin: 0 auto !important;
page-break-after: always !important; page-break-after: always !important;
box-shadow: none !important; box-shadow: none !important;
width: var(--page-w); width: var(--page-w);
height: var(--page-h); height: var(--page-h);
} }
.tablet-element { .tablet-element {
position: absolute !important; /* 絕對不能被 Bootstrap 覆蓋為 static */ position: absolute !important; /* 絕對不能被 Bootstrap 覆蓋為 static */
color: black !important; /* 確保文字是黑色的 */ color: black !important; /* 確保文字是黑色的 */
} }
/* 列印時的紙張大小與頁面歸零 */ /* 列印時的紙張大小與頁面歸零 */
@media print { @media print {
@page { @page {
/* 動態套用您設定的紙張寬高 */ /* 動態套用您設定的紙張寬高 */
size: ${this.paper.width}mm ${this.paper.height}mm portrait; size: ${size.width}mm ${size.height}mm portrait;
margin: 0; margin: 0;
} }
body, html { body, html {
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
} }
body{ body{
--page-w: 210mm; --page-w: ${size.width}mm;
--page-h: 297mm; --page-h: ${size.height}mm;
--font-max: 22pt; --font-max: 22pt;
--bg-padding: 0; --bg-padding: 0;
} }
} }
</style> </style>
</head> </head>
<body> <body>
${canvasHtml} ${canvasHtml}
</body> </body>
</html> </html>
`; `;
w.document.write(htmlContent); w.document.write(htmlContent);
w.document.close(); w.document.close();
@@ -430,8 +473,8 @@
}); });
$("#paperSize").append("<option value='newsize' >新增尺寸</option>") $("#paperSize").append("<option value='newsize' >新增尺寸</option>")
this.allSize.forEach(x => { this.allSize.forEach(x => {
$("#paperSize").append("<option value='" + x.name + "' " + x.selected + ">" + x.name+"("+x.width + "*" + x.height + ")</option>") $("#paperSize").append("<option value='" + x.id + "'>" + x.name + "(" + x.width + "*" + x.height + ")</option>")
$("#printSize").append("<option value='" + x.name + "' " + x.selected + ">" + x.name+"("+x.width + "*" + x.height + ")</option>"); $("#printSize").append("<option value='" + x.id + "'>" + x.name+"("+x.width + "*" + x.height + ")</option>");
}) })
}, },
async getElements() { async getElements() {
@@ -499,13 +542,23 @@
this.elements.length = 0; this.elements.length = 0;
let obj = this.elements let obj = this.elements
let s = this.allStyle.find(x => x.styleID == id) let s = this.allStyle.find(x => x.styleID == id)
console.log("changeStyle:", s);
let size = this.allSize.find(x => x.id == s.paperSize)
this.paper.width = size.width
this.paper.height = size.height
console.log("changeStyle:", size);
console.log("changeStyle:", this.paper);
$("#paperSize").val(s.paperSize); $("#paperSize").val(s.paperSize);
$("#backendInp").val(s.backendImg); $("#backendInp").val(s.backendImg);
$("#printSize").val(s.printSize); $("#printSize").val(s.printSize);
$("#printMode").val(s.printMode); $("#printMode").val(s.printMode);
$("#perPage").val(s.printPageCount); $("#perPage").val(s.printPageCount);
let img = this.bg.find(x => x.name == s.backendImg); let img = this.bg.find(x => x.name == s.backendImg);
$(".tablet-paper").css({ 'background-image': 'url(' + (img ? img.path : "") + ')', 'background-size': '100% 100%' }) $(".tablet-paper").css({
'background-image': 'url(' + (img ? img.path : "") + ')', 'background-size': '100% 100%',
width: (size?size.width:self.paper.width) + "mm",
height: (size?size.height:self.paper.height) + "mm",
})
await axios await axios
.post(HTTP_HOST + 'api/tablet/GetStyleDetailData', { styleID: id }) .post(HTTP_HOST + 'api/tablet/GetStyleDetailData', { styleID: id })
.then(response => { .then(response => {
@@ -559,16 +612,16 @@
}, },
changePaper() { changePaper() {
let paperSize = $("#paperSize").val() let paperSize = $("#paperSize").val()
console.log(paperSize); let self = this;
if (paperSize == "newsize") { if (paperSize == "newsize") {
$("#sizeDiv").attr("style","display:"); $("#sizeDiv").attr("style","display:");
} else { } else {
let size = this.allSize.find(x => x.name == paperSize) console.log("paperSize:",paperSize);
let size = this.allSize.find(x => x.id == paperSize)
$(".tablet-paper").css({ $(".tablet-paper").css({
"background-color": "white", "background-color": "white",
width: size.width + "mm", width: (size?size.width:self.paper.width) + "mm",
height: size.height + "mm", height: (size?size.height:self.paper.height) + "mm",
}); });
} }
}, },
@@ -625,11 +678,18 @@
const roster = this.elements.find(e => e.type === 'roster'); const roster = this.elements.find(e => e.type === 'roster');
const names = roster ? roster.text.split('\n').filter(s => s.trim()) : []; const names = roster ? roster.text.split('\n').filter(s => s.trim()) : [];
const pages = Math.max(1, Math.ceil(names.length / this.rosterLimit)); const pages = Math.max(1, Math.ceil(names.length / this.rosterLimit));
let style = this.allStyle.find(x => x.styleID == this.styleID);
let size = this.allSize.find(x=>x.id==style.paperSize);
console.log(this.paper,size);
//for (let p = 0; p < pages; p++) { //for (let p = 0; p < pages; p++) {
// const $paper = $('<div class="tablet-paper"></div>') // const $paper = $('<div class="tablet-paper"></div>')
// .css({ "background-color": "red", width: this.paper.width + 'mm', height: this.paper.height + 'mm' }); // .css({ "background-color": "red", width: this.paper.width + 'mm', height: this.paper.height + 'mm' });
let $paper = $(".tablet-paper").css({ "background-color": "white", width: this.paper.width + 'mm', height: this.paper.height + 'mm', position: "absolute" }); let $paper = $(".tablet-paper").css({
"background-color": "white",
width: (size ? size.width : this.paper.width) + 'mm',
height: (size ? size.height : this.paper.height) + 'mm',
position: "absolute"
});
//const slice = names.slice(p * this.rosterLimit, (p + 1) * this.rosterLimit); //const slice = names.slice(p * this.rosterLimit, (p + 1) * this.rosterLimit);
const slice = names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit); const slice = names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit);
this.elements.forEach(el => { this.elements.forEach(el => {

View File

@@ -67,10 +67,10 @@
/* 名單金字塔佈局容器 */ /* 名單金字塔佈局容器 */
.roster-container { .roster-container {
width: 100%; height: 100%; width: 100%; height: 600px;
writing-mode: vertical-rl; /* 直書 */ writing-mode: vertical-rl; /* 直書 */
display: flex; display: flex;
//flex-direction: column; /* 雖然是直書,但物理上我們是將「上層區」和「下層區」垂直堆疊 */ /*flex-direction: column;*/ /* 雖然是直書,但物理上我們是將「上層區」和「下層區」垂直堆疊 */
flex-direction: row; /* 上下分層 (Top / Bottom) */ flex-direction: row; /* 上下分層 (Top / Bottom) */
align-items: center; /* 左右置中對齊 */ align-items: center; /* 左右置中對齊 */
justify-content: center; justify-content: center;
@@ -95,12 +95,13 @@
.roster-name { .roster-name {
text-orientation: upright; text-orientation: upright;
font-weight: bold; /*font-weight: bold;*/
white-space: nowrap; white-space: nowrap;
line-height: 1.2; line-height: 1.2;
font-family: 'Kaiti', serif; font-family: 'Kaiti', serif;
/* 確保名字本身不會佔據過多寬度導致間距看起來很大 */ /* 確保名字本身不會佔據過多寬度導致間距看起來很大 */
/* width: fit-content;*/ width: fit-content;
height:200px;
} }