Files
17168ERP/web/admin/print/print_multi_new.aspx
2026-03-27 19:24:04 +08:00

630 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="print_multi_new.aspx.cs" Inherits="admin_print_print_multi_new" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link href="~/js/bootstrap5/bootstrap.min.css" rel="stylesheet" />
<%--<link href="~/js/fontawesome6/css/all.css" rel="stylesheet" />--%>
<link href="~/js/mdi-font/css/materialdesignicons.min.css" rel="stylesheet" />
<link href="~/js/vuetify_ez.css" rel="stylesheet" />
<link href="~/js/sweetalert2/sweetalert2.min.css" rel="stylesheet" />
<link href="~/admin/Templates/TBS5ADM001/css/Style.css" rel="stylesheet" />
<link href="~/admin/item/css/floating.css" rel="stylesheet" />
<link href="~/admin/item/css/tablet-design.css" rel="stylesheet" />
<style>
@media print {
/*@page {
size: 398mm 216mm portrait;
margin: 0;
}*/
html, body, form, .full-home, #printArea, .canvas-area {
height: auto !important;
width: 210mm !important;
overflow: visible !important; /*必須為 visible */
margin: 0 !important;
padding: 0 !important;
/* display: block !important;*/
}
.no-print, #customMenu {
display: none !important;
}
}
</style>
</head>
<body>
<span class="btn btn-primary no-print" onclick="Printer.print()">列印</span>
<div class="d-flex full-home" style="height: auto;width:100%;">
<div id="printArea" class="printArea" style="width: auto; height: auto">
<%-- <div class="tablet-paper">
</div>--%>
<%-- </div>--%>
</div>
</div>
<div id="customMenu" style="display: none; position: absolute; background-color: #f9f9f9; border: 1px solid #ccc; padding: 10px; z-index: 1000; opacity: 1;">
<div class="container">
<div class="row">
<div class="col-6">
<div class="form-floating mb-3">
<input type="text" id="textX" class="form-control form-control-sm mb-2" onchange="Printer.changeLocation()" />
<label class="small" for="textX">X軸位置</label>
</div>
</div>
<div class="col-6">
<div class="form-floating mb-3">
<input type="text" id="textY" class="form-control form-control-sm mb-2" onchange="Printer.changeLocation()" />
<label class="small" for="textY">Y軸位置</label>
</div>
</div>
<div class="col-6">
<div class="form-floating mb-3">
<input type="text" id="fontSize" class="form-control form-control-sm mb-2" onchange="Printer.changeSize()" />
<label class="small" for="fontSize">字體大小</label>
</div>
</div>
<div class="col-6">
<div class="form-floating mb-3">
<input type="text" id="textWidth" class="form-control form-control-sm mb-2" onchange="Printer.changeWidth()" />
<label class="small" for="textWidth">文字寬度</label>
</div>
</div>
</div>
</div>
</div>
<form id="form1" runat="server">
</form>
</body>
<script src="<%=ResolveUrl("~/js/bootstrap5/js/bootstrap.bundle.min.js")%>"></script>
<script src="<%=ResolveUrl("~/js/jquery-4.0.0.min.js")%>"></script>
<script src="<%=ResolveUrl("~/js/vue.min.js")%>"></script>
<script src="<%=ResolveUrl("~/js/vuetify.min.js")%>"></script>
<script src="<%=ResolveUrl("~/js/axios.min.js")%>"></script>
<script src="<%=ResolveUrl("~/js/moment.min.js")%>"></script>
<script src="<%=ResolveUrl("~/js/sweetalert2/sweetalert2.all.min.js") %>"></script>
<script src="<%=ResolveUrl("~/admin/Templates/TBS5ADM001/js/Script.js")%>"></script>
<script src="<%=ResolveUrl("~/admin/item/jquery-ui/jquery-ui.min.js")%>"></script>
<script>
const Printer = {
selected: null,
http_host: "",
allStyle: [],
allSize: [],
allStyleDetails: [],
printData: [],
tabletElement: [],
bg: [
{ name: "黃1", path: "../../admin/print/html/tablet-1.svg" },
{ name: "黃2", path: "../../admin/print/html/tablet-1B.svg" },
{ name: "紅1", path: "../../admin/print/html/tablet-2.svg" },
{ name: "紅2", path: "../../admin/print/html/tablet-2B.svg" }
],
async init() {
console.log("init");
let HTTP_HOST = "<%=UrlHost()%>";
this.http_host = HTTP_HOST;
await Promise.all([
this.getPaperSize(),
this.getTabletElement(),
this.getTabletStyles(),
this.getAllStyleDetails()
]);
this.getData();
},
async getAllStyleDetails() {
await axios
.post(this.http_host + 'api/tablet/GetStyleDetailData', {})
.then(response => {
if (response.status == "200") {
let data = response.data;
if (data.result == "Y") {
let details = data.data;
this.allStyleDetails = details;
console.log(data.data);
}
}
}
);
},
async getPaperSize() {
let self = this
await axios
.post(this.http_host + 'api/tablet/GetPaperSize', {})
.then(response => {
//if (response.result=="Y") {
if (response.status == "200") {
let data = response.data;
this.allSize = data.data;
}
}
)
},
async getTabletElement() {
await axios
.post(this.http_host + 'api/tablet/GetTabletElement', {})
.then(response => {
//if (response.result=="Y") {
if (response.status == "200") {
let data = response.data;
this.tabletElement = data.data;
}
}
);
},
async getTabletStyles() {
await axios
.post(this.http_host + 'api/tablet/GetStyleData', {})
.then(response => {
//if (response.result=="Y") {
if (response.status == "200") {
let data = response.data;
this.allStyle = data.data;
}
}
);
},
async getData() {
let list = localStorage.getItem("list")
let param = [];
let data = JSON.parse(list)
data.forEach(x => {
param.push({ order_no: x.order_no, num: x.num });
});
await axios
.post(this.http_host + 'api/orderdetail/GetDetailToPrint', { param: param })
.then(response => {
if (response.status == 200) {
this.printData = response.data;
this.render();
this.print();
}
});
},
render() {
let self = this;
let canvas = $(` <div class="canvas-area flex-grow-1 overflow-auto d-flex flex-row align-items-center position-relative" ></div>`)
this.printData.forEach(x => {
let style = this.allStyle.find(y => y.styleID == x.style);
console.log("style:", style);
let size = this.allSize.find(y => y.paperID == style.paperSize);
let tabletpaper = $(` <div class="tablet-paper">
</div>`)
let img = self.bg.find(y => y.name == style.backendImg);
tabletpaper.css({
"background-color": "white",
width: size.width + 'mm',
height: size.height + 'mm',
position: "relative",
"margin-bottom": "0",
"background-image": `url(${img.path})`,
'background-size': '100% 100%',
"break-after": "page",
"page-break-after": "always"
});
let tablet = JSON.parse(x.f_num_tablet);
let mid_items = tablet.mid_items;
let left_items = tablet.left_items;
console.log("mid_items:", mid_items)
let details = this.allStyleDetails.filter(y => y.styleID == x.style);
let mid = [];
mid_items.forEach(y => {
mid.push(y.fam_name);
});
details.forEach(d => {
if (d.isActive != "hidden") {
console.log(d);
let html = "";
if (d.elementID = 'title1') {
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": "Kaiti",
"letter-spacing": "0.1em",
"column-gap": "1px",
"row-gap": "1px",
"align-items": "center",
});
mid.forEach(z => {
let $span = $(`<span>${z}</span>`).css({
display: "block",
"min-height": `${d.textHeight}px`,
"max-height": `${d.height}px`,
width: `${d.textWidth}px`,
"text-align": "justify",
"text-align-last": "justify",
"margin-bottom": "20px",
"margin-left": "5px",
"text-justify": "inter-character",
"z-index": 10000,
});
$namelist.append($span);
});
html = $namelist;
}
let content = $(`<div class="tablet-element vertical-text "></div>`)
.css({
position: "absolute", left: d.startX + "mm", top: d.startY + "mm", fontSize: d.fontSize + 'pt', fontFamily: d.fontFamily, "z-index": 9999, visibility: d.isActive
//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);
tabletpaper.append(content);
}
})
$(canvas).append(tabletpaper);
console.log(size.width,size.height);
let pageStyle = `<style id="pageStyle">
@media print{
:host {
width:auto !important;
height:auto !important;
}
@page {
size:210mm 297mm !important;
margin:0;
}
.tablet-paper {
width: ${size.width}mm ;
height: ${size.height}mm;
position: relative !important;
display: block !important;
break-after: page !important;
page-break-after: always !important;
margin: 0 !important;
border: none !important;
}
}
`
$("#pageStyle").remove();
$("head").append(pageStyle
);
$(canvas).css("width", "210" + "mm");
$(canvas).css("height","297"+ "mm");
$("#printArea").append(canvas);
//this.bindKeyEvent();
});
$(".tablet-element").draggable({
start(e, ui) {
console.log("gogogogo");
},
stop(e, ui) {
console.log(ui.position.left, ui.position.top);
}
});
$(".tablet-element").on("click", function (e) {
e.preventDefault();
console.log(this);
self.selected = $(this);
let fontSize = $(this).css("fontSize");
let fontFamily = $(this).css("fontFamily");
let left = $(this).css("left");
let top = $(this).css("top");
console.log(fontSize, fontFamily, left, top);
});
$(".tablet-element").on("contextmenu", function (e) {
e.preventDefault();
console.log(this);
self.selected = $(this);
let fontSize = $(this).css("fontSize");
let fontFamily = $(this).css("fontFamily");
let left = $(this).css("left");
let top = $(this).css("top");
console.log(fontSize, fontFamily, left, top);
$("#customMenu").css({
top: e.pageY + "px",
left: (e.pageX + 30) + "px"
});
left = left.replace("px", "");
top = top.replace("px", "");
$("#textX").val(self.fix2(left * 0.265));
$("#textY").val(self.fix2(top * 0.265));
$("#fontSize").val(fontSize);
//$("#textWidth").val("");
// Show the custom div
$("#customMenu").show();
});
},
fix2(val) {
return Number.parseFloat(val).toFixed(2);
},
async changeLocation() {
console.log(this.selected);
$(this.selected).css("left", $("#textX").val() + "mm");
$(this.selected).css("top", $("#textY").val() + "mm");
},
async changeSize() {
$(this.selected).css("fontSize", $("#fontSize").val())
},
async changeWidth() {
let nameList = $(this.selected).find(".nameList").first();
console.log("nameList:", nameList);
if (nameList) {
let spans = $(nameList).find("span");
console.log(spans);
if (spans) {
spans.each(function (index, x) {
$(x).css("width", $("#textWidth").val())
});
}
}
},
print() {
//setTimeout(() => { window.print(); }, 1000);
let s = this.allStyle.find(x => x.styleID == this.styleID)
let size = this.allSize.find(x => x.paperID = "20260311160935")
let w = window.open('', '_blank');
if (!w) {
alert("請允許瀏覽器開啟彈出式視窗!");
return;
}
// 1. 抓取您原本網頁定義的 HTTP_HOST (您的全域變數),如果沒有則抓取當前網址
let hostUrl = typeof this.http_host !== 'undefined' ? this.http_host : (window.location.protocol + "//" + window.location.host);
if (!hostUrl.endsWith('/')) hostUrl += '/';
// 2. 抓出畫布完整的 HTML
let canvasHtml = $("#printArea").html();
// 3. 【關鍵修復】把所有的 "../../" 替換成完整的網址,解決底圖破圖問題!
canvasHtml = canvasHtml.replace(/\.\.\/\.\.\//g, hostUrl);
// 4. 組合列印視窗的 HTML
let htmlContent = `
<!DOCTYPE html>
<html>
<head>
<title>預覽列印 - 牌位設計</title>
<meta charset="utf-8">
<style>
/* 基礎變數與強制背景列印 */
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
/* 螢幕預覽樣式 */
@media screen {
body {
background-color: #525659;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0;
}
#printArea {
width: auto !important;
height: auto !important; /* 移除 100vh 限制 */
}
.tablet-paper {
margin-bottom: 30px;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
background-color: white;
}
}
/* 關鍵:修正 Ctrl+P 空白問題 */
@media print {
@page {
size: 210mm 272mm portrait;
margin: 0;
}
html, body, form, .full-home, #printArea, .canvas-area {
height: auto !important;
width: auto !important;
overflow: visible !important; /* 必須為 visible */
margin: 0 !important;
padding: 0 !important;
display: block !important;
}
.canvas-area{
display:flex !important;
flex-direction: row !important;
}
.no-print, #customMenu {
display: none !important;
}
.tablet-paper {
position: relative !important;
display: block !important;
break-after: page !important;
page-break-after: always !important;
margin: 0 !important;
border: none !important;
}
}
/* 元素排版樣式 */
.tablet-paper {
position: relative;
overflow: hidden;
background-repeat: no-repeat;
background-size: 100% 100%;
}
.tablet-element {
position: absolute !important;
white-space: nowrap;
}
.vertical-text {
writing-mode: vertical-rl !important;
-webkit-writing-mode: vertical-rl !important;
}
</style>
<style>
/* 名單金字塔佈局容器 */
.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;
}
.vertical-text {
writing-mode: vertical-rl !important;
-webkit-writing-mode: vertical-rl !important;
text-orientation: mixed !important;
}
/* 【修復底圖沒出現】強制印出背景設定 */
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
/* 【修復排版沒照設定】覆寫原本 HTML 裡的 fixed並防止 Bootstrap 洗掉 absolute */
.tablet-paper {
position: relative !important;
margin: 0 auto !important;
page-break-after: always !important;
box-shadow: none !important;
width: var(--page-w);
height: var(--page-h);
}
.tablet-element {
position: absolute !important; /* 絕對不能被 Bootstrap 覆蓋為 static */
color: black !important; /* 確保文字是黑色的 */
}
.ancestor-wrapper {
/* 重置為橫向流,這樣 column 才會是真正的上下堆疊 */
writing-mode: horizontal-tb;
display: flex;
flex-direction: column;
align-items: center; /* 水平置中 */
justify-content: center;
width: fit-content;
}
.main-name {
line-height: 1.2;
/* 保持大字 */
}
.sub-text {
font-size: 0.6em; /* 縮小字體 */
line-height: 1.2;
margin-top: 4px; /* 與上方林張的間距 */
white-space: nowrap; /* 避免自動換行 */
writing-mode:vertical-rl
}
</style>
</head>
<body>
${canvasHtml}
</body>
</html>
`;
w.document.write(htmlContent);
w.document.close();
// 5. 將等待時間稍微拉長至 1.2 秒,確保大張的 SVG 底圖與外部字體(如標楷體)下載完畢
setTimeout(() => {
w.focus();
w.print();
}, 1200);
},
//bindKeyEvent() {
// let element = document.querySelectorAll(".tablet-element");
// element.forEach(y => {
// console.log("QQ:",y);
// $(y).on("keydown", (e) => {
// console.log(e.key);
// e.preventDefault();
// let top = parseInt(y.style.top);
// let left = parseInt(y.style.left);
// switch (e.key) {
// case 'ArrowUp': top = top - 10; break;
// case 'ArrowDown': top = top + 10; break;
// case 'ArrowLeft': left = left - 10; break;
// case 'ArrowRight': left = left + 10; break;
// }
// y.style.top = top + 'px';
// y.style.left = left + 'px';
// })
// })
//}
}
$(document).click(function (e) {
if (!$("#customMenu").is(e.target) && $("#customMenu").has(e.target).length === 0) {
$("#customMenu").hide();
}
});
$(() => Printer.init());
</script>
</html>