Files
17168ERP/web/admin/print/tablet_edit/editorNewOne.html
T
2026-07-30 18:01:35 +08:00

2152 lines
114 KiB
HTML
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.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="../../../js/bootstrap5/bootstrap.min.css" rel="stylesheet" />
<link href="../../../js/fontawesome6/css/all.min.css" rel="stylesheet" />
<link href="../../item/jquery-ui/jquery-ui.min.css" rel="stylesheet" />
<link href="../../../js/vuetify.css" rel="stylesheet" />
<link href="../../../js/mdi-font/css/materialdesignicons.min.css" rel="stylesheet" />
<script src="../../../js/vue.min.js"></script>
<script src="../../../js/vuetify.min.js"></script>
<script src="../../../js/axios.min.js"></script>
<script src="../../../js/bootstrap5/js/bootstrap.bundle.min.js"></script>
<script src="../../../js/jquery-4.0.0.min.js"></script>
<script src="../../item/jquery-ui/jquery-ui.min.js"></script>
<style>
.div_border {
border: 1px solid black;
}
.work_space {
min-height: 40vh;
min-width: 70vw;
max-width: 90vw;
max-height: 50vh;
}
body {
width: 90vw;
height: 90vh
}
li {
font-size: 16px;
list-style: none;
margin-top: 1rem;
}
</style>
</head>
<body>
<div class="d-flex full-home no-print" style="height: 100vh;width:100%;">
<div id="designArea" class="designArea" style="width: 100%; height: auto">
<div class="row mt-2">
<div class="col-3">
版型設定:
<select id="styleSel" onchange="designer.changeStyle()" class="form-select">
<option value="">請選擇版型</option>
</select>
</div>
<div class="col-2">
<button class="btn btn-primary" onclick="designer.save(event)">儲存牌位</button>
</div>
<div class="col-5">
<!-- 原生縮放橫軸容器 -->
<div style="display: flex; align-items: center; padding: 8px; background-color: #f5f5f5; border-radius: 4px; margin-bottom: 12px; font-family: sans-serif;">
<span style="margin-right: 8px; font-size: 14px;" onclick="designer.minusZoomScalee()"></span>
<!-- 使用 input type="range",綁定 zoomScale -->
<!-- min="0.2" (20%), max="1.5" (150%), step="0.05" (每次滑動5%) -->
<input type="range"
v-model="designer.zoomScale"
min="0.0"
max="1.0"
step="0.05"
style="flex-grow: 1; cursor: pointer;" id="scaleSlider">
<span style="margin-left: 8px; font-size: 14px;" onclick="designer.plusZoomScalee()"></span>
<!-- 顯示百分比數值 -->
<span id="scaleSize" style="margin-left: 12px; min-width: 45px; font-weight: bold; font-size: 14px; text-align: right;">
</span>
</div>
</div>
</div>
<div class="row work_space">
<div class="col-7 div_border printArea" style="height:80vh;overflow-y:auto;"></div>
<div class="col-5 div_border" style="font-size:12px;">
<div class="row" style="height: 50%; overflow-y: auto;">
<div style="display:inline-block;width:100%;height:30px;background-color:darkgray;">
<div class="row" >
<div class="col-12">
<h3>已選</h3>
</div>
<div class="col-10">
<div class="form-floating mb-3">
<input class="form-control" type="text" id="custom_txt" placeholder="請輸入名稱" />
<label for="custom_txt">請輸入名稱</label>
</div>
</div>
<div class="col-2">
<button type="button" class="custom_add" onclick="designer.addNewItem()" style="width:20px;height:20px;font-size:25px">+</button>
</div>
</div>
<ul class="selectedItem" style="margin-left:1rem;margin-right:1rem;">
</ul>
</div>
</div>
<div class="row" style="height:50%;overflow-y:auto;">
<div style="display:inline-block;width:100%;height:30px;background-color:darkgray;">
<div class="row" >
<div class="col-12">
<h3>可選</h3>
</div>
<div class="col-10">
<div class="form-floating mb-3">
<input class="form-control" type="text" id="filter_txt"
onkeyup="designer.filterToSel()" placeholder="篩選條件" />
<label for="filter_txt">篩選條件</label>
</div>
</div>
<div class="col-2">
<button type="button" data-bs-toggle="modal" data-bs-target="#familyModal" title="新增親友">
<i class="mdi mdi-account-multiple-plus" style="font-size:30px;"></i>
</button>
</div>
</div>
<ul class="canselectItem" style="margin-left: 1rem;margin-right:1rem;">
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade hide" id="familyModal" tabindex="-1" aria-labelledby="familyModalLabel" aria-hidden="true" style="z-index:10000 !important;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">新增親友</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-12">
<div class="form-floating mb-3">
<input class="form-control" type="text" id="family_txt"
placeholder="姓名" />
<label for="family_txt">姓名</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<select class="form-select" id="deceased_sel"
placeholder="存歿">
<option value="False"></option>
<option value="True">歿</option>
</select>
<label for="deceased_sel">存歿</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<input class="form-control" type="text" id="title_txt"
placeholder="稱謂" />
<label for="title_txt">稱謂</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">關閉</button>
<button type="button" class="btn btn-primary" onclick="designer.saveFamily()">存檔</button>
</div>
</div>
</div>
</div>
<div class="position-fixed top-50 start-50 p-3" style="z-index: 10001" >
<div id="liveToast" class="toast hide " role="alert" aria-live="assertive"
data-bs-delay="3000" aria-atomic="true"
style="color:white;background-color:dimgray;opacity:1;font-size:24px;">
<div class="toast-header">
<!--<img src="..." class="rounded me-2" alt="...">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>-->
<strong class="me-auto">提示訊息</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body" id="toast_body">
</div>
</div>
</div>
<script type="text/javascript">
const designer = {
zoomScale:1,
code: {
style: []
},
selected: {
style: ""
},
actitem: "",
pageSize: "",
defaultStyle: "",
isAllSelected: false,
dialog: false,
addDialog: false, // 新增項目對話框
addDialogType: '', // 'Y' 或 'N'
newItemText: '', // 新增項目的文字
noSpace: true, // 不加空格 checkbox(預設勾選)
deleteDialog: false, // 確認刪除對話框
deleteIndex: -1, // 要刪除的 index
deleteType: '', // 'Y' 或 'N'
deleteMemberName: '', // 要刪除的名稱(顯示用)
phrases: [], // 常用片語
familyMembers: [], // 親友名單
tabletItem: {}, // 傳入的資料(信眾/牌位資訊)
item: {},
family_deceased_Y_selected: [], // 超渡/超薦/超冤名單
family_deceased_N_selected: [], // 消災/陽上名單
family_left_title: [],
family_right_title: [],
family_address: "",
allStyle: [],
allStyleDetails: [],
allSize: [],
tabletElement: [],
http_host: "",
printData: [],
selectedElement: {},
selectStyle: {},
properTitle: [],//正名
leftProperTitle: [],//左正名
rightProperTitle: [],//右正名
address: "",//地址
alive: [],//陽上
tabletType: "A",
http_host: "",
follower_id: "",
toast: null,
modal: null,
bg: [
{ name: "超薦1", path: "../../../admin/print/html/tablet-1_new.svg" },
{ name: "超薦2", path: "../../../admin/print/html/tablet-1B_new.svg" },
{ name: "消災1", path: "../../../admin/print/html/tablet-2.svg" },
{ name: "消災2", path: "../../../admin/print/html/tablet-2B.svg" },
{ name: "消災3", path: "../../../admin/print/html/tablet-2C.svg" },
{ name: "消災4", path: "../../../admin/print/html/tablet-2D.svg" }
],
async init() {
// 載入常用片語
fetch('phrases.json')
.then(res => res.json())
.then(data => { this.phrases = data; })
.catch(err => console.error('載入 phrases.json 失敗:', err));
this.toast = new bootstrap.Toast($('#liveToast'));
this.modal = new bootstrap.Modal($('#familyModal'));
//this.toast.show();
$("#scaleSize").html((this.zoomScale * 100) + "%")
$("#scaleSlider").val(this.zoomScale)
$("#scaleSlider").on("change", function () {
this.zoomScale = $(this).val()
$("#scaleSize").html(Math.floor(Number($(this).val()) * 100) + "%");
$(".tablet-paper").css("transform",`scale(${Number($(this).val())})`);
})
},
async minusZoomScalee() {
this.zoomScale = this.zoomScale - 0.05
$("#scaleSize").html(Math.floor(this.zoomScale * 100) + "%")
$("#scaleSlider").val(this.zoomScale)
$(".tablet-paper").css("transform", `scale(${Number(this.zoomScale)})`);
},
async plusZoomScalee() {
this.zoomScale = this.zoomScale + 0.05
$("#scaleSize").html(Math.floor(this.zoomScale * 100) + "%")
$("#scaleSlider").val(this.zoomScale)
$(".tablet-paper").css("transform", `scale(${Number(this.zoomScale)})`);
}, //加撈activity_relating的資料,用來判斷是否有人數上限 activity_num actItem_num
async getActItem() {
//console.log(this.tabletItem.has_chao_limit, this.tabletItem.has_yang_limit, this.tabletItem.chao_limit_count, this.tabletItem.yang_limit_count)
axios
.post('/api/tablet/GetActItem', { itemNum: this.actitem })
.then(response => {
if (response.data.result == "Y" && response.data.data) {
this.pageSize = response.data.data.pageSize;
this.defaultStyle = response.data.data.defaultStyle;
this.selected.style = response.data.data.defaultStyle;
this.selectStyle.style = response.data.data.defaultStyle;
//console.log("GetActItem:",this.defaultStyle)
$("#styleSel").val(this.defaultStyle);
}
}
);
},
async getAllStyleDetails() {
this.allStyleDetails.length = 0;
await axios
.post('/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() {
this.allSize.length = 0;
let self = this
await axios
.post('/api/tablet/GetPaperSize', {})
.then(response => {
//if (response.result=="Y") {
if (response.status == "200") {
let data = response.data;
this.allSize = data.data;
}
//$("#paperSizeSel").val("0003");
})
},
async getTabletElement() {
this.tabletElement.length = 0;
await axios
.post('/api/tablet/GetTabletElement', {})
.then(response => {
//if (response.result=="Y") {
if (response.status == "200") {
let data = response.data;
this.tabletElement = data.data;
}
});
},
async getTabletStyles() {
this.allStyle.length = 0;
await axios
.post('/api/tablet/GetStyleData', {})
.then(response => {
if (response.status == "200") {
let data = response.data;
if (data.data) {
data.data.forEach(x => {
if (x.styleID !== "000001") {
this.allStyle.push(x);
$("#styleSel").append(`<option value="${x.styleID}">${x.name}</option`);
}
})
}
}
});
},
async getData() {
//console.log("getData:", this.tabletItem)
this.printData.length = 0;
this.properTitle.length = 0
this.leftProperTitle.length = 0;
this.rightProperTitle.length = 0;
this.alive.length = 0;
//let list = localStorage.getItem("list")
let param = [];
let self = this;
param.push({ order_no: this.tabletItem.order_no, num: this.tabletItem.num })
await axios
.post('/api/orderdetail/GetDetailToPrint', { param: param })
.then(response => {
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)
}
if (x.f_num_tablet != "") {
let tablet = JSON.parse(x.f_num_tablet);
let mid_items = tablet?.mid_items;
let left_items = tablet?.left_items;
let mid_left = tablet?.mid_left;
let mid_right = tablet?.mid_right;
let address = tablet?.addr_items===undefined?"":tablet?.addr_items;
if (address === "") {
if (x.address!="") {
address = x.address
}
}
self.family_deceased_Y_selected.length = 0;
self.family_deceased_N_selected.length = 0;
self.family_address = address;
self.family_left_title.length = 0;
self.family_right_title.length = 0;
Object.assign(self.family_deceased_N_selected, mid_items);
Object.assign(self.family_deceased_Y_selected, left_items);
Object.assign(self.family_left_title, mid_left);
Object.assign(self.family_right_title, mid_right);
self.family_address = address;//tablet.addr_items;
//let mid = [];
//family_deceased_Y_selected family_deceased_N_selected
if (mid_items) {
mid_items.forEach(y => {
self.properTitle.push(y.fam_name);
});
}
if (mid_left) {
mid_left.forEach(y => {
self.leftProperTitle.push(y.fam_name);
})
}
if (mid_right) {
mid_right.forEach(y => {
self.rightProperTitle.push(y.fam_name);
})
}
if (address) {
self.address = address;
}
//let left = []
if (left_items) {
left_items.forEach(y => {
self.alive.push(y.fam_name);
});
}
}
});
this.render();
}
});
},
async render() {
$(".printArea").empty();
$(".selectedItem").empty();
$(".canselectItem").empty();
let self = this;
self.tabletType = "A";
let canvas = $(` <div class="canvas-area flex-grow-1 overflow-auto d-flex flex-row align-items-center position-relative" ></div>`)
let nowPageWidth = 0;
let tabletpaper;
//列印紙張的尺寸
let mid = [];
Object.assign(mid, self.properTitle);
let left = []
Object.assign(left, self.alive);
let ltitle = [];
let rtitle = [];
let myAddress = [];
Object.assign(ltitle, self.leftProperTitle);
Object.assign(rtitle, self.rightProperTitle);
if (self.address != "") {
myAddress.push(self.address)
}
//this.printData.forEach(x => {
let x = self.selectStyle;
let style = self.allStyle.find(y => y.styleID == (x.style ? x.style : $("#styleSel").val()));
//console.log("style:",style)
if (x.style) {
} else {
self.selectStyle.style = style.styleID;
self.selected.style = style.styleID;
}
//用來印內容的尺寸
let size = self.allSize.find(y => y.paperID == style.paperSize);
let pageSize = self.allSize.find(y => y.paperID == style.paperSize);
//包含空白用來折的尺寸
let printSize = self.allSize.find(y => y.paperID == style.printSize);
//let scaleSize = 1
//let documentHeight = document.documentElement.scrollHeight;
//if ((Number(pageSize.height) / 0.265) >= (Number(documentHeight) * 0.8)) {
// scaleSize = (Number(documentHeight) * 0.8) / (Number(pageSize.height) / 0.265)
//}
//$("#scaleSize").html((scaleSize * 100) + "%")
tabletpaper = $(` <div class="tablet-paper" style="transform-origin:top left;transform:scale(${this.zoomScale})"></div>`)
let img = self.bg.find(y => y.name == style.backendImg);
let details = this.allStyleDetails.filter(y => y.styleID == style.styleID);
let ancestorFontSize = 16;
details.forEach(d => {
if (d.isActive != "hidden") {
let element = self.tabletElement.find(y => y.elementID == d.elementID);
let newFontSize = self.scaleFontSize(mid, d.fontSize, d.width);
let html = "";
let alive = "";
let yang = "";
let bi = "";
if (d.elementID === 'title1') {
if (mid.length == 0) {
mid.push("請選擇")
}
// 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, colWidth);
} else if (d.elementID === "alive") {
self.tabletType = "B";
if (left.length == 0) {
left.push("請選擇");
}
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, colWidth);
} else if (d.elementID === "titletriangle") {
if (mid.length == 0) {
mid.push("請選擇")
}
html = self.renderRoster(mid, d)
} else if (d.elementID === "combined" || d.elementID === "tricombined") {
ancestorFontSize = d.fontSize;
if (mid.length == 0) {
mid.push("請選擇")
}
html = self.renderCombined(mid, d);
} else if (d.elementID === "lefttitle") {
if (ltitle.length == 0) {
ltitle.push("請選擇")
}
html = self.renderNormalWithSpace(ltitle, d)
} else if (d.elementID === "righttitle") {
if (rtitle.length == 0) {
rtitle.push("請選擇")
}
html = self.renderNormalWithSpace(rtitle, d)
} else if (d.elementID == "address") {
if (myAddress.length == 0) {
myAddress.push("請選擇")
}
html = self.renderNormal(myAddress.join(""), d)
}
if (newFontSize < 10) {
newFontSize = 10;
}
let content = $(`<div class="tablet-element vertical-text ">${element.sampleContent}</div>`)
.css({
position: "absolute", left: d.startX + "mm", top: d.startY + "mm", fontSize: newFontSize + 'px',
fontFamily: d.fontFamily, "font-weight": "bold", "z-index": 9999, visibility: d.isActive,
"align-items": "center"
})
.html(html);
tabletpaper.append(content);
html = "";
alive = "";
yang = "";
bi = "";
}
});
self.pageStyle = `<style id="pageStyle">
{
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
.vertical-text {
writing-mode: vertical-rl !important;
-webkit-writing-mode: vertical-rl !important;
text-orientation: mixed !important;
}
body{
overflow: visible !important;
}
@media print{
:host {
width:auto !important;
height:auto !important;
}
@page {
size:${printSize.width}mm ${printSize.height}mm !important;
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;
}
.no-print, #customMenu {
display: none !important;
}
.canvas-area{
display:flex !important;
flex-direction: row !important;
justify-content:center !important;
align-items:center !important;
}
.tablet-paper {
position: relative !important;
display: block !important;
break-inline:avoid;
page-break-inline:avoid;
break-after: page !important;
page-break-after: always !important;
margin: 0 !important;
border: none !important;
background-repeat: no-repeat;
background-size: 100% 100%;
}
.tablet-element {
position: absolute !important;
white-space: normal;
color: black !important; /* 確保文字是黑色的 */
}
}
/* 名單金字塔佈局容器 */
.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: normal;
line-height: 1.2;
font-family: DFKai-sb;
/* 確保名字本身不會佔據過多寬度導致間距看起來很大 */
width: fit-content;
}
/* 【修復底圖沒出現】強制印出背景設定 */
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
.ancestor-wrapper {
/* 重置為橫向流,這樣 column 才會是真正的上下堆疊 */
writing-mode: horizontal-tb;
display: flex;
flex-direction: column;
align-items: center; /* 水平置中 */
justify-content: felx-start;
width: fit-content;
}
.main-name {
line-height: 1.2;
/* 保持大字 */
}
.sub-text {
font-size: ${ancestorFontSize}px ;/*0.8em;*/ /* 縮小字體 */
line-height: 1.2;
margin-top: 4px; /* 與上方林張的間距 */
white-space: normal; /* 避免自動換行 */
writing-mode:vertical-rl
}
.canvas-area{
width: ${pageSize.width}mm;
height: ${pageSize.height}mm;
break-after: page;
page-break-after: always;
display:flex !important;
justify-content:center;
}
.tablet-paper{
background-color: white;
display: inline-block;
width: ${size.width}mm !important;
height: ${size.height}mm !important;
position: relative;
margin-bottom: 0;
background-image: url(${img.path});
background-size: 100% 100%;
break-inline:avoid;
page-break-inline:avoid;
}
.tablet-element {
cursor: move;
padding: 4px;
color: black !important; /* 強制墨水為黑色 */
border: 1px solid transparent;
transition: writing-mode 0.3s, transform 0.2s;
}
.tablet-element:hover { border-color: rgba(13, 110, 253, 0.3); }
.tablet-paper:last-child { page-break-after: auto; }
.tablet-element span{
z-index: 999;
}
.selectedItem .canselectItem{
font-size: 18px;
}
.selectedItem,.rounded-pill .canselectItem,.rounded-pill{
font-size: 12px;
}
</style>
`
$("#pageStyle").remove();
$("head").append(self.pageStyle);
$(canvas).append(tabletpaper);
$(".printArea").append(canvas);
$(".tablet-element").on("click", function (e) {
e.preventDefault();
self.selectedElement = $(this);
let element = $(this).children().first();
self.renderItem(element);
});
},
async renderItem(element) {
let self = this;
$(".selectedItem").empty();
$(".canselectItem").empty();
if (element.hasClass("liveList")) {//陽上
//抓未往生的人出來
//self.familyMembers;
self.alive.forEach(x => {
let f = self.familyMembers.find(z => z.fam_name == x);
$(".selectedItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="selected">${x}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class="itemRemove" style="float:right ;margin-left:2rem;cursor:pointer;" >&minus;</span>
<span class="itemUp" style="float:right ;margin-left:2rem;cursor:pointer;">&#8593;</span>
<span class="itemDown" style="float:right ;margin-left:2rem;cursor:pointer;">&#8595;</span></li>`);
});
let items = []
self.familyMembers.forEach(x => {
if (x.deceased == false)
items.push(x.fam_name);
});
let finalItem = items.filter(y => !self.alive.includes(y))
finalItem.forEach(yy => {
let f = self.familyMembers.find(z => z.fam_name == yy);
$(".canselectItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="unselect">${yy}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != ''&&f.fam_title!=null) ? 'p-1':''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class='itemAppend' style="float:right;cursor:pointer;">&#43;<span></li>`);
});
} else if (element.hasClass("address")) {//地址
//console.log(self.address);
$(".selectedItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="selected">${self.address==undefined?"":self.address}</span>
<span class="itemRemove" style="float:right ;margin-left:2rem;cursor:pointer;" >&minus;</span>`);
} else if (element.hasClass("lefttitle")) {//左正名
self.leftProperTitle.forEach(x => {
let f = self.familyMembers.find(z => z.fam_name == x);
$(".selectedItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="selected">${x}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class="itemRemove" style="float:right ;margin-left:2rem;cursor:pointer;" >&minus;</span>`);
})
let items = [];
self.familyMembers.forEach(x => {
if (self.tabletType == "A" && x.deceased == false) {
items.push(x.fam_name);
} else if (self.tabletType == "B" && x.deceased == true) {
items.push(x.fam_name);
}
});
let finalItem = items.filter(y => !self.leftProperTitle.includes(y))
finalItem.forEach(yy => {
let f = self.familyMembers.find(z => z.fam_name == yy);
$(".canselectItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="unselect">${yy}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class='itemAppend' style="float:right;cursor:pointer;">&#43;<span></li>`);
});
} else if (element.hasClass("righttitle")) {//右正名
self.rightProperTitle.forEach(x => {
let f = self.familyMembers.find(z => z.fam_name == x);
$(".selectedItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="selected">${x}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class="itemRemove" style="float:right ;margin-left:2rem;cursor:pointer;" >&minus;</span>`);
})
let items = [];
self.familyMembers.forEach(x => {
if (self.tabletType == "A" && x.deceased == false) {
items.push(x.fam_name);
} else if (self.tabletType == "B" && x.deceased == true) {
items.push(x.fam_name);
}
});
let finalItem = items.filter(y => !self.rightProperTitle.includes(y))
finalItem.forEach(yy => {
let f=self.familyMembers.find(z=>z.fam_name==yy);
$(".canselectItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="unselect">${yy}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class='itemAppend' style="float:right;cursor:pointer;">&#43;<span></li>`);
});
} else {//其他都是正名
if (element.hasClass("ancestor-wrapper")) {//多姓氏合併,只允許用打的
self.properTitle.forEach(y => {
let f = self.familyMembers.find(z => z.fam_name == y);
$(".selectedItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="selected">${y}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class="itemRemove" style="float:right ;margin-left:2rem;cursor:pointer;" >&minus;</span>
<span class="itemUp" style="float:right ;margin-left:2rem;cursor:pointer;">&#8593;</span>
<span class="itemDown" style="float:right ;margin-left:2rem;cursor:pointer;">&#8595;</span></li>`);
});
} else if (element.hasClass("nameList") || element.hasClass("rosterList")) {//
self.properTitle.forEach(y => {
let f = self.familyMembers.find(z => z.fam_name == y);
$(".selectedItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="selected">${y}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class="itemRemove" style="float:right ;margin-left:2rem;cursor:pointer;" >&minus;</span>
<span class="itemUp" style="float:right ;margin-left:2rem;cursor:pointer;">&#8593;</span>
<span class="itemDown" style="float:right ;margin-left:2rem;cursor:pointer;">&#8595;</span></li>`);
})
let items = []
self.familyMembers.forEach(x => {
if (self.tabletType == "A" && x.deceased == false) {
items.push(x.fam_name);
} else if (self.tabletType == "B" && x.deceased == true) {
items.push(x.fam_name);
}
});
let finalItem = items.filter(y => !self.properTitle.includes(y))
finalItem.forEach(yy => {
let f = self.familyMembers.find(z => z.fam_name == yy);
$(".canselectItem").append(`<li><span style="opacity:0.5">卍</span>
<span class="unselect">${yy}</span>
<span>
<i class="rounded-pill rounded-3 ${(f && f.fam_title && f.fam_title != '' && f.fam_title != null) ? 'p-1' : ''}" style="background-color:lightgray">
${(f && f.fam_title != null) ? f.fam_title : ""}</i>
</span>
<span class='itemAppend' style="float:right;cursor:pointer;">&#43;<span></li>`);
});
}
}
self.bindEvent();
},
async addNewItem() {//已選輸入框按加號
//console.log("addNewItem:",$("#custom_txt").val())
if ($("#custom_txt").val()=="") {
alert("名稱不可空白")
return false
}
let self = this;
let target = $(self.selectedElement).children().first();
let error=""
//let fam = self.familyMembers.find(x => x.fam_name == target.text());
if (target.hasClass("liveList")) {
self.alive.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.alive.push($("#custom_txt").val());
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: $("#custom_txt").val(),
fam_file: "",
nospace: true,
num: 0,
option_break: false
}
self.family_deceased_Y_selected.push(obj);
} else if (target.hasClass("nameList") || target.hasClass("rosterList")) {
self.properTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.properTitle.push($("#custom_txt").val());
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: $("#custom_txt").val(),
fam_file: "",
nospace: true,
num: 0,
option_break: false
}
self.family_deceased_N_selected.push(obj);
} else if (target.hasClass("ancestor-wrapper")) {
//再來判斷是否已有這個名稱
self.properTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
if (self.properTitle.length >= 2) {
alert("多姓氏合併必須第一個名稱為姓,第二個名稱為氏歷代祖先之類,且最多兩個名稱");
} else {
self.properTitle.push($("#custom_txt").val());
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: $("#custom_txt").val(),
fam_file: "",
nospace: true,
num: 0,
option_break: false
}
self.family_deceased_N_selected.push(obj);
}
} else if (target.hasClass("lefttitle")) {
self.leftProperTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.leftProperTitle.length = 0;
self.family_left_title.length = 0;
self.leftProperTitle.push($("#custom_txt").val());
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: $("#custom_txt").val(),
fam_file: "",
nospace: true,
num: -1,
option_break: false
}
self.family_left_title.push(obj);
//self.family_deceased_N_selected.push(obj);
//}
} else if (target.hasClass("righttitle")) {
self.rightProperTitle.forEach(x => {
if (x == $("#custom_txt").val()) {
error = "名稱已存在"
}
})
if (error != "") {
alert(error)
return false
}
self.rightProperTitle.length = 0;
self.family_right_title.length = 0;
self.rightProperTitle.push($("#custom_txt").val());
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: $("#custom_txt").val(),
fam_file: "",
nospace: true,
num: 0,
option_break: false
}
self.family_right_title.push(obj);
//self.family_deceased_N_selected.push(obj);
//}
} else if (target.hasClass("address")) {
self.address = ($("#custom_txt").val());
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: $("#custom_txt").val(),
fam_file: "",
nospace: true,
num: 0,
option_break: false
}
self.family_address = self.address
//self.family_deceased_N_selected.push(obj);
}
$("#custom_txt").val("");
self.renderItem($(self.selectedElement).children().first());
self.rerenderPart();
},
async bindEvent() {
let self = this;
$(".itemAppend").off("click");
$(".itemRemove").off("click");
$(".itemUp").off("click");
$(".itemDown").off("click");
$(".itemAppend").on("click", function (e) {
let curr = $(e.currentTarget.parentElement).find(".unselect").first();
let target = self.familyMembers.find(x => x.fam_name == curr.text());
if (target) {//liveList nameList rosterList 要判斷yang_limit_count chao_limit_count
if ($(self.selectedElement).children().first().hasClass("liveList")) {
if (self.tabletItem.has_yang_limit && self.tabletItem.yang_limit_count > 0) {
if (self.alive.length + 1 > self.tabletItem.yang_limit_count) {
$("#toast_body").html(`陽上人數不得超過${self.tabletItem.yang_limit_count}人`)
self.toast.show();
return
}
}
self.alive.push(target.fam_name);
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: target.fam_name,
fam_file: "",
nospace: true,
num: target.num,
option_break: false
}
self.family_deceased_Y_selected.push(obj);
} else if ($(self.selectedElement).children().first().hasClass("nameList") ||
$(self.selectedElement).children().first().hasClass("rosterList")) {
if (self.tabletItem.has_chao_limit && self.tabletItem.chao_limit_count > 0) {
if (self.properTitle.length + 1 > self.tabletItem.chao_limit_count) {
$("#toast_body").html(`祈福或超渡人數不得超過${self.tabletItem.chao_limit_count}人`)
self.toast.show();
return
}
}
self.properTitle.push(target.fam_name);
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: target.fam_name,
fam_file: "",
nospace: true,
num: target.num,
option_break: false
}
self.family_deceased_N_selected.push(obj);
} else if ($(self.selectedElement).children().first().hasClass("lefttitle")) {
self.leftProperTitle.push(target.fam_name);
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: target.fam_name,
fam_file: "",
nospace: true,
num: target.num,
option_break: false
}
self.family_left_title.push(obj);
} else if ($(self.selectedElement).children().first().hasClass("righttitle")) {
self.rightProperTitle.push(target.fam_name);
let obj = {
IsShuWen: false,
deceased: false,
fam_gender: "",
fam_name: target.fam_name,
fam_file: "",
nospace: true,
num: target.num,
option_break: false
}
self.family_right_title.push(obj);
}
}
self.renderItem($(self.selectedElement).children().first())
self.rerenderPart();
});
$(".itemRemove").on("click", function (e) {
let curr = $(e.currentTarget.parentElement).find(".selected").first();
if ($(self.selectedElement).children().first().hasClass("liveList")) {
self.alive = self.alive.filter(x => x !== curr.text());
self.family_deceased_Y_selected = self.family_deceased_Y_selected.filter(x => x.fam_name != curr.text());
} else if ($(self.selectedElement).children().first().hasClass("nameList") ||
$(self.selectedElement).children().first().hasClass("rosterList")) {
self.properTitle = self.properTitle.filter(x => x !== curr.text());
self.family_deceased_N_selected = self.family_deceased_N_selected.filter(x => x.fam_name != curr.text());
} else if ($(self.selectedElement).children().first().hasClass("ancestor-wrapper")) {//
self.properTitle = self.properTitle.filter(x => x !== curr.text());
self.family_deceased_N_selected = self.family_deceased_N_selected.filter(x => x.fam_name != curr.text());
} else if ($(self.selectedElement).children().first().hasClass("address")) {
self.address = curr.text();
self.family_address = curr.text()
} else if ($(self.selectedElement).children().first().hasClass("lefttitle")) {
self.leftProperTitle = self.leftProperTitle.filter(x => x !== curr.text());
self.family_left_title = self.family_left_title = (x => x.fam_name !== curr.text());
} else if ($(self.selectedElement).children().first().hasClass("righttitle")) {
self.rightProperTitle = self.rightProperTitle.filter(x => x !== curr.text());
self.family_right_title = self.family_right_title = (x => x.fam_name !== curr.text());
}
self.renderItem($(self.selectedElement).children().first());
self.rerenderPart();
});
$(".itemUp").on("click", function (e) {
let curr = $(e.currentTarget.parentElement).find(".selected").first();
if ($(self.selectedElement).children().first().hasClass("liveList")) {
let index = self.alive.indexOf(curr.text());
if (index == 0) {
} else {
[self.alive[index - 1], self.alive[index]] = [self.alive[index], self.alive[index - 1]];
[self.family_deceased_Y_selected[index - 1], self.family_deceased_Y_selected[index]] =
[self.family_deceased_Y_selected[index], self.family_deceased_Y_selected[index - 1]];
}
} else if ($(self.selectedElement).children().first().hasClass("nameList") ||
$(self.selectedElement).children().first().hasClass("rosterList")) {
let index = self.properTitle.indexOf(curr.text());
if (index == 0) {
} else {
[self.properTitle[index - 1], self.properTitle[index]] = [self.properTitle[index], self.properTitle[index - 1]];
[self.family_deceased_N_selected[index - 1], self.family_deceased_N_selected[index]] =
[self.family_deceased_N_selected[index], self.family_deceased_N_selected[index - 1]];
}
} else if ($(self.selectedElement).children().first().hasClass("ancestor-wrapper")) {
let index = self.properTitle.indexOf(curr.text());
if (index == 0) {
} else {
[self.properTitle[index - 1], self.properTitle[index]] = [self.properTitle[index], self.properTitle[index - 1]];
[self.family_deceased_N_selected[index - 1], self.family_deceased_N_selected[index]] =
[self.family_deceased_N_selected[index], self.family_deceased_N_selected[index - 1]];
}
} else if ($(self.selectedElement).children().first().hasClass("address")) {
//self.address = curr.text();
} else if ($(self.selectedElement).children().first().hasClass("lefttitle")) {
let index = self.leftProperTitle.indexOf(curr.text());
if (index == 0) {
} else {
[self.leftProperTitle[index - 1], self.leftProperTitle[index]] = [self.leftProperTitle[index], self.properTitle[index - 1]];
[self.family_left_title[index - 1], self.family_left_title[index]] =
[self.family_left_title[index], self.family_left_title[index - 1]];
}
} else if ($(self.selectedElement).children().first().hasClass("righttitle")) {
let index = self.rightProperTitle.indexOf(curr.text());
if (index == 0) {
} else {
[self.rightProperTitle[index - 1], self.rightProperTitle[index]] = [self.rightProperTitle[index], self.properTitle[index - 1]];
[self.family_right_title[index - 1], self.family_right_title[index]] =
[self.family_right_title[index], self.family_right_title[index - 1]];
};
}
self.renderItem($(self.selectedElement).children().first());
self.rerenderPart();
});
$(".itemDown").on("click", function (e) {
let curr = $(e.currentTarget.parentElement).find(".selected").first();
if ($(self.selectedElement).children().first().hasClass("liveList")) {
let index = self.alive.indexOf(curr.text());
if (index == self.alive.length - 1) {
} else {
[self.alive[index], self.alive[index + 1]] = [self.alive[index + 1], self.alive[index]];
[self.family_deceased_Y_selected[index], self.family_deceased_Y_selected[index + 1]] =
[self.family_deceased_Y_selected[index + 1], self.family_deceased_Y_selected[index]];
}
} else if ($(self.selectedElement).children().first().hasClass("nameList") ||
$(self.selectedElement).children().first().hasClass("rosterList")) {
let index = self.properTitle.indexOf(curr.text());
if (index == self.properTitle.length - 1) {
} else {
[self.properTitle[index], self.properTitle[index + 1]] = [self.properTitle[index + 1], self.properTitle[index]];
[self.family_deceased_N_selected[index], self.family_deceased_N_selected[index + 1]] =
[self.family_deceased_N_selected[index + 1], self.family_deceased_N_selected[index]];
}
} else if ($(self.selectedElement).children().first().hasClass("ancestor-wrapper")) {
let index = self.properTitle.indexOf(curr.text());
if (index == self.properTitle.length - 1) {
} else {
[self.properTitle[index], self.properTitle[index + 1]] = [self.properTitle[index + 1], self.properTitle[index]];
[self.family_deceased_N_selected[index], self.family_deceased_N_selected[index + 1]] =
[self.family_deceased_N_selected[index + 1], self.family_deceased_N_selected[index]];
}
} else if ($(self.selectedElement).children().first().hasClass("address")) {
//self.address = curr.text();
} else if ($(self.selectedElement).children().first().hasClass("lefttitle")) {
let index = self.leftProperTitle.indexOf(curr.text());
if (index == 0) {
} else {
[self.leftProperTitle[index], self.leftProperTitle[index + 1]] = [self.leftProperTitle[index + 1], self.properTitle[index]];
[self.family_left_title[index], self.family_left_title[index + 1]] =
[self.family_left_title[index + 1], self.family_left_title[index]];
}
} else if ($(self.selectedElement).children().first().hasClass("righttitle")) {
let index = self.rightProperTitle.indexOf(curr.text());
if (index == 0) {
} else {
[self.rightProperTitle[index], self.rightProperTitle[index + 1]] = [self.rightProperTitle[index + 1], self.properTitle[index]];
[self.family_right_title[index], self.family_right_title[index + 1]] =
[self.family_right_title[index + 1], self.family_right_title[index]];
};
}
self.renderItem($(self.selectedElement).children().first());
self.rerenderPart();
});
},
async rerenderPart() {
let self = this;
let x = self.selectStyle;
let style = self.allStyle.find(y => y.styleID == x.style);
//用來印內容的尺寸
let size = self.allSize.find(y => y.paperID == style.paperSize);
let pageSize = self.allSize.find(y => y.paperID == style.printSize);
//包含空白用來折的尺寸
let printSize = self.allSize.find(y => y.paperID == style.printSize);
let details = self.allStyleDetails.filter(y => y.styleID == x.style);
if ($(self.selectedElement).children().first().hasClass("liveList")) {
$(self.selectedElement).empty();
//要重新render陽上
let left = []
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;
//}
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") ||
$(self.selectedElement).children().first().hasClass("address")) {
let data = []
if ($(self.selectedElement).children().first().hasClass("lefttitle")) {
$(self.selectedElement).empty();
Object.assign(data, self.leftProperTitle);
let d = details.find(y => y.elementID == "lefttitle");
let html = self.renderNormalWithSpace(data, d);
//console.log("html:",html);
$(self.selectedElement).append(html);
} else if ($(self.selectedElement).children().first().hasClass("righttitle")) {
$(self.selectedElement).empty();
Object.assign(data, self.rightProperTitle);
let d = details.find(y => y.elementID == "righttitle");
let html = self.renderNormalWithSpace(data, d);
$(self.selectedElement).append(html);
} else if ($(self.selectedElement).children().first().hasClass("address")) {
$(self.selectedElement).empty();
data.push(self.address);
let d = details.find(y => y.elementID == "address");
let html = self.renderNormal(data, d);
$(self.selectedElement).append(html);
}
//this.printData.forEach(x => {
} else {
let mid = []
Object.assign(mid, self.properTitle);
//要重新render陽上
if ($(self.selectedElement).children().first().hasClass("nameList")) {
$(self.selectedElement).empty();
let d = details.find(y => y.elementID == "title1");
let newfontSize = self.scaleFontSize(mid, d.fontSize, d.textWidth)
// 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,0);
$(self.selectedElement).append(html);
} else if ($(self.selectedElement).children().first().hasClass("rosterList")) {
$(self.selectedElement).empty();
let d = details.find(y => y.elementID == "titletriangle");
let newfontSize = self.scaleFontSize(mid, d.fontSize, d.textWidth)
if (newfontSize < 10) {
newfontSize = 10;
}
self.selectedElement.css("font-size", newfontSize+"px");
let html = self.renderRoster(mid, d);
$(self.selectedElement).append(html);
} else if ($(self.selectedElement).children().first().hasClass("ancestor-wrapper")) {
$(self.selectedElement).empty();
let d = details.find(y => y.elementID == "combined");
let newfontSize = self.scaleFontSize(mid, d.fontSize, d.textWidth)
if (newfontSize < 10) {
newfontSize = 10;
}
self.selectedElement.css("font-size", newfontSize+"px");
let html = self.renderCombined(mid, d);
$(self.selectedElement).append(html);
}
}
},
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);
},
changeStyle() {
let self = this;
this.selected.style = $("#styleSel").val();
this.selectStyle.style = $("#styleSel").val();
//console.log("changeStyle:",this.selectStyle);
this.render();
},
fix2(val) {
return Number.parseFloat(val).toFixed(2);
},
renderNormalWithSpace(mid, d) {
//console.log("renderNormal:", d)
let self = this;
let txt = $(`<div class='${d.elementID}'></div>`).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",
});
//console.log("吼優:",mid);
mid.forEach(x => {
let dd = $(`<div>${x}</div>`).css({
"margin-bottom": "10px",
"margin-left": "5px",
"text-justify": "inter-character",
"white-space": "pre-line",
});
$(txt).append(dd);
});
return txt;
},
renderNormal(mid, d) {
//console.log("renderNormal:", d)
let self = this;
let txt = $(`<div class='${d.elementID}'>${mid}</div>`).css({
"z-index": 9999, visibility: d.visibility,
width: d.width, height: d.height
});
return txt;
},
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",
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",
"column-gap": `${d.textSpan}px`,
"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";
let $span= $(`<span style="font-size: ${item.size}pt;">${item.name}</span>`).css({
display: "block",
"min-height": minH, // 強制多行長名撐滿高度,避免其他名字墊底
"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);
})
//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) {
let $namelist = $(`<div class='liveList'></div>`).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": "0.1em",
"column-gap": `${el.textSpan}px`,
//"row-gap": "1px",
"align-items": "center",
});
let self = this;
names.forEach(n => {
$namelist.append(self.renderLiveSpan(n, el,rowWidth))
})
return $namelist;
},
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: "auto",
"line-height": "1.1",
"text-align": alignStyle, // ★ 動態套用對齊方式
"text-align-last": alignStyle, // ★ 動態套用最後一行對齊方式
"margin-left": "5px",
"text-justify": "inter-character",
"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) {
if (!names.length) return '';
const mid = names.length === 1 ? 1 : Math.floor(names.length / 2);
const top = names.slice(0, mid);
const bot = names.slice(mid);
const size = this.autoScale(names, el.fontSize);
let h = $(`<div class="rosterList"></div>`).css(
{
width: el.width, height: el.height,
"writing-mode": "vertical-rl", /* 直書 */
display: "flex",
"flex-direction": "row", /* 上下分層 (Top / Bottom) */
"align-items": "center", /* 左右置中對齊 */
"justify-content": "start",
gap: "20px",
});
h.append(this.renderNameGroups(top, el));
if (bot.length) h.append(this.renderNameGroups(bot, el));
return h
},
renderCombined(mid, d) {
ancestorFontSize = d.fontSize;
newFontSize = d.fontSize;
const parts = mid.join("\n").split('\n');
html = `<div class="ancestor-wrapper" style="width:${d.width}px !important;height:${d.height}px !important;" >
<span class="main-name" >${parts[0] || ''}</span>
<span class="sub-text">${parts[1] || ''}</span>
</div>`;
return html
},
renderNameGroups(items, el) {
let g = $(`<div class="rosterspan"></div>`).css({
"display": "flex",
"flex-direction": "column",
"justify-content": "center",
"height": el.textHeight,
"white-space": "pre-line"
})
items.forEach(x => {
g.append($(`<div class="" style="letter-spacing: 10px;text-align: justify; text-align-last: justify;">${x}</div>`))//font-size:${el.fontSize}pt;
})
return g;
},
async autoScale(names, base) {
const max = Math.max(...names.map(n => n.length), 0);
return max > 5 ? Math.max(base * (5 / max), base * 0.6) : base;
},
scaleFontSize(txtContent, fontSize, elementWidth) {
let font = this.getTextSize("講", parseInt(fontSize));
//自動判斷縮小字型的規則,要同時縮小間距
//if (dStyle.elementID === 'title1') {//表示是一般正名,也就是牌位正中間的一般格式,排法是上下上下
//console.log("shit:", txtContent.join(","), (Math.ceil(txtContent.length / 2) * font.width) * 2, parseFloat(elementWidth))
if ((Math.ceil(txtContent.length / 2) * font.width) * 2 > parseFloat(elementWidth)) {
let newfontSize = parseInt(fontSize) - 1;
if (newfontSize <= 10) {
return newfontSize;
}
//console.log("newfontSize:",newfontSize)
return this.scaleFontSize(txtContent, newfontSize, elementWidth);
}
return fontSize;
},
getTextInfo(text, font) {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
context.font = font;
const metrics = context.measureText(text);
return metrics
},
getTextWidth(text, font) {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
context.font = font;
const metrics = context.measureText(text);
return metrics;
},
getTextSize(txt, fontsize) {
let span = document.createElement("span");
span.style.visibility = "hidden";
span.style.position = "absolute";
span.style.fontSize = fontsize + "px";
span.style.fontFamily = "";
span.innerText = txt;
document.body.appendChild(span);
let result = {
width: span.offsetWidth,
height: span.offsetHeight
}
document.body.removeChild(span);
return result;
},
filterToSel() {
let allList = document.querySelector(".canselectItem").querySelectorAll("li"); //$(".canselectItem").querySelectorAll("li");
allList.forEach(x => {
let curr = $(x).children().eq(1);
x.setAttribute("style", "display:none")
$(x).css("style", "display:none")
if (curr && curr.text().includes($("#filter_txt").val())) {
x.setAttribute("style", "display:")
} else {
}
});
},
async saveFamily() {
let self = this;
if (this.follower_id == "") {
//console.log("follower_id:", this.follower_id);
return false
}
let pro_order_detail =
{
// num: this.editedItem.num,//原先載入時的家人num
//f_num: this.editedItem.f_num_selected.val, //儲存時的家人num
//leader: this.follower_id,
//appellation_id: this.editedItem.appellation_id_selected.val, //儲存時的稱謂
follower_num: this.follower_id,
fam_name: $("#family_txt").val(),
fam_title:$("#title_txt").val(),
deceased: $("#deceased_sel").val() == "False" ? false : true,
num: 0
}
this.modal.toggle();
//$(this.modal).toggle();
//$(this.modal).addClass("hide");
//console.log(pro_order_detail);
//console.log(this.modal);
//$(this.modal).hide();
axios
.post('/api/tablet/saveFamily', pro_order_detail)
.then(response => {
//console.log(response);
if (response.status == 200 && response.data.result == "Y") {
let data = response.data;
self.familyMembers.push(data.data);
//console.log(self.familyMembers);
//alert("新增成功");
self.modal.toggle();
if (self.selectedElement) {
let element = $(self.selectedElement).children().first();
self.renderItem(element);
}
$("#toast_body").html(`${"新增成功"}`)
$("#family_txt").val("");
$("#title_txt").val("");
$("#deceased_sel").val("False")
self.toast.show();
} else {
}
});
},
async save(event) {
event.preventDefault()
//console.log("tabletItem:", designer.tabletItem);
//拚牌位的內容
//console.log("AA")
let self = this;
let tablet = {
mid_items: designer.family_deceased_N_selected,
left_items: designer.family_deceased_Y_selected,
style: designer.selectStyle.style,
mid_left: designer.family_left_title,
mid_right: designer.family_right_title,
addr_items: designer.family_address
}
//console.log("BB")
var pro_order_detail =
{
num: designer.tabletItem.num <= 0 ? null : designer.tabletItem.num,
order_no: designer.tabletItem.order_no,
parent_num: designer.tabletItem.parent_num,
actItem_num: designer.tabletItem.actitem_num_selected.val,
f_num: designer.tabletItem.f_num_selected.val,
f_num_tablet: JSON.stringify(tablet), //designer.tabletItem.f_num_tablet,//牌位內容
print_id: designer.tabletItem.print_id,
address: designer.tabletItem.address,
from_id: designer.tabletItem.from_id_selected.val,
from_id_tablet: designer.tabletItem.from_id_tablet,
start_date: designer.tabletItem.category == 1 ? designer.tabletItem.start_date : null,
due_date: designer.tabletItem.category == 1 ? designer.tabletItem.due_date : null,
extend_date: designer.tabletItem.category == 1 ? designer.tabletItem.extend_date : null,
price: designer.tabletItem.price,
qty: designer.tabletItem.qty,
//total: this.editedItem.total,
keyin1: designer.tabletItem.keyin1_selected.val,
demo: designer.tabletItem.demo,
pay: designer.tabletItem.pay,
pay_date: designer.tabletItem.pay_date,
customize_data: designer.tabletItem.customize_data,
style: designer.selectStyle.style
}
//console.log("CC")
await axios
.post('/api/order/SaveDetailData', pro_order_detail)
.then(response => {
let tablet_data = {
mid_items: self.family_deceased_N_selected,
left_items: self.family_deceased_Y_selected,
style: self.selectStyle.style,
mid_left: self.family_left_title,
mid_right: self.family_right_title,
addr_items: self.family_address
}
const ret = {
source: 'editorNew.btn.click',
data: {
tabletItem: this.tabletItem,
tablet_data: tablet_data,
style: self.selectStyle.style
}
};
window.parent.postMessage(ret, '/');
//$.toast({
// heading: 'Success',
// text: 'Your action was completed successfully!',
// showHideTransition: 'slide', // fade, slide, plain
// icon: 'success', // info, warning, error, success
// position: 'top-left',
// loaderBg: '#4CAF50', // loader background color
// hideAfter: 3000 // auto close after 3 seconds
//});
//$(".toast-body").empty();
//$(".toast-body").html("ok");
//this.toast.show();
});
//console.log("pro_order_detail:",pro_order_detail);
},
reset() {
designer.code = { style: [] };
designer.selected = {
style: ""
};
designer.actitem = "";
designer.pageSize = "";
designer.defaultStyle = "";
designer.isAllSelected = false;
designer.dialog = false;
designer.addDialog = false; // 新增項目對話框
designer.addDialogType = ''; // 'Y' 或 'N'
designer.newItemText = ''; // 新增項目的文字
designer.noSpace = true; // 不加空格 checkbox(預設勾選)
designer.deleteDialog = false; // 確認刪除對話框
designer.deleteIndex = -1; // 要刪除的 index
designer.deleteType = ''; // 'Y' 或 'N'
designer.deleteMemberName = ''; // 要刪除的名稱(顯示用)
designer.phrases = []; // 常用片語
designer.familyMembers = []; // 親友名單
designer.tabletItem = {}; // 傳入的資料(信眾/牌位資訊)
designer.item = {};
designer.family_deceased_Y_selected = []; // 超渡/超薦/超冤名單
designer.family_deceased_N_selected = []; // 消災/陽上名單
designer.family_left_title = [];
designer.family_right_title = [];
designer.allStyle = [];
designer.allStyleDetails = [];
designer.allSize = [];
designer.tabletElement = [];
designer.http_host = "";
designer.printData = [];
designer.selectedElement = {};
designer.selectStyle = {};
designer.properTitle = [];//正名
designer.leftProperTitle = [];//左正名
designer.rightProperTitle = [];//右正名
designer.address = "";//地址
designer.alive = [];//陽上
designer.tabletType = "A";
designer.follower_id = "";
$(".printArea").empty();
$(".selectItem").empty();
$(".canselectItem").empty();
},
async resetScreen() {
$(".printArea").empty();
$(".selectItem").empty();
$(".canselectItem").empty();
}
}
$(() => $(window).on("message", function (event) {
//designer.toast.show();
//designer.reset();
let e = event.originalEvent;
//console.log("QOO");
//console.log("傳過來了");
if (e.origin === window.parent.location.origin) {
//console.log('editor.html - origin check passed');
//console.log('editor.html - received data:', event.data);
const receivedData = e.data;
// console.log("receivedData:", receivedData)
designer.family_deceased_Y_selected = [];
designer.family_deceased_N_selected = [];
designer.family_left_title = [];
designer.family_right_title = [];
designer.http_host = receivedData.host;
designer.follower_id = receivedData.follower_id;
//console.log("三小:", receivedData.tabletItem)
if (receivedData.tabletItem) {
//console.log('editor.html - updating tabletItem:', receivedData.tabletItem);
designer.tabletItem = receivedData.tabletItem;
if (receivedData.familyMembers) {
designer.familyMembers = receivedData.familyMembers; // 更新 familyMembers
//console.log("app mounted, window message: ", this.familyMembers, receivedData);
}
//if (receivedData.tabletItem) {
// designer.tabletItem = receivedData.tabletItem;
//}
//console.log("actitem:", receivedData.tabletItem.actitem_num_selected.val);
if (receivedData.tabletItem.style) {
designer.selected.style = receivedData.tabletItem.style;
} else {
designer.selected.style = "";
}
designer.actitem = receivedData.tabletItem.actitem_num_selected.val;
//console.log("actitem:",receivedData.tabletItem.actitem_num_selected.val);
// 處理 f_num_tablet 資料
if (designer.tabletItem.f_num_tablet) {
try {
const data = JSON.parse(designer.tabletItem.f_num_tablet);
if (designer.item_type === 'B') {
// B類型:超渡、超薦等
designer.family_deceased_Y_selected = data.mid_items || [];
designer.family_deceased_N_selected = data.left_items || [];
designer.family_deceased_Y_selected.forEach(item => {
if (item.IsShuWen === undefined) {
//Vue.set(item, 'IsShuWen', false);
}
});
if (designer.family_deceased_Y_selected.length > 0) {
designer.isAllSelected = this.family_deceased_Y_selected.every(member => member.IsShuWen === true);
} else {
designer.isAllSelected = false;
}
} else {
// A類型:消災、陽上等
designer.family_deceased_Y_selected = [];
designer.family_deceased_N_selected = data.mid_items || [];
designer.family_deceased_N_selected.forEach(item => {
if (item.IsShuWen === undefined) {
//Vue.set(item, 'IsShuWen', false);
}
});
if (designer.family_deceased_Y_selected.length > 0) {
designer.isAllSelected = designer.family_deceased_N_selected.every(member => member.IsShuWen === true);
} else {
designer.isAllSelected = false;
}
//console.log(this.family_deceased_N_selected)
}
} catch (e) {
console.error('解析牌位資料時發生錯誤:', e);
}
}
designer.resetScreen();
designer.getActItem().then(() => delay(300)).then(() => designer.getData());
}
}
//console.log(e.origin, window.parent.location.origin)
})
);
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
$(() => designer.init().then(() => designer.getTabletElement().
then(() => designer.getPaperSize().
then(() => designer.getTabletStyles().
then(designer.getAllStyleDetails())))));
</script>
</body>
</html>