From b02fc8ec522c8c0f0dd66143ff76c98ec323b1da Mon Sep 17 00:00:00 2001 From: minom Date: Wed, 26 Aug 2026 18:03:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/admin/follower/tablet_template.html | 200 ++++++++++-------------- 1 file changed, 82 insertions(+), 118 deletions(-) diff --git a/web/admin/follower/tablet_template.html b/web/admin/follower/tablet_template.html index 9f83980..4e1da29 100644 --- a/web/admin/follower/tablet_template.html +++ b/web/admin/follower/tablet_template.html @@ -669,114 +669,81 @@ $("#canselectItem").empty(); let currentData = [], editLabel = "未定義區塊"; - // 🌟 1. 判斷是否為超薦牌位 (利用 item_type === 'B' 或是依據底圖名稱防呆) - let isChaojian = false; - if (this.item_type === 'B' || (typeof VueApp !== 'undefined' && VueApp.item_type === 'B') || (this.tabletItem && this.tabletItem.item_type === 'B')) { - isChaojian = true; - } else { - let style = this.allStyle.find(y => y.styleID == this.selectStyle); - if (style && style.name && style.name.includes("超薦")) { - isChaojian = true; - } - } - - // 預設生死條件:超薦要死者(true),消災要活者(false) + let isChaojian = (this.item_type === 'B' || (typeof VueApp !== 'undefined' && VueApp.item_type === 'B') || (this.tabletItem && this.tabletItem.item_type === 'B')) || + (this.allStyle.find(y => y.styleID == this.selectStyle)?.name || "").includes("超薦"); let requireDeceased = isChaojian; - // 🌟 2. 判斷當前編輯區塊 - if (element.hasClass("liveList")) { - editLabel = "陽上"; - currentData = this.alive; - requireDeceased = false; // 🚨 陽上永遠只能選活者 - } - else if (element.hasClass("nameList") || element.hasClass("rosterList") || element.hasClass("ancestor-wrapper")) { - editLabel = "正名"; - currentData = this.properTitle; - } - else if (element.hasClass("lefttitle")) { - editLabel = "左正名"; - currentData = this.leftProperTitle; - } - else if (element.hasClass("righttitle")) { - editLabel = "右正名"; - currentData = this.rightProperTitle; - } - else if (element.hasClass("address")) { - editLabel = "地址"; - currentData = this.address ? [this.address] : []; - } + if (element.hasClass("liveList")) { editLabel = "陽上"; currentData = this.alive; requireDeceased = false; } + else if (element.hasClass("nameList") || element.hasClass("rosterList") || element.hasClass("ancestor-wrapper")) { editLabel = "正名"; currentData = this.properTitle; } + else if (element.hasClass("lefttitle")) { editLabel = "左正名"; currentData = this.leftProperTitle; } + else if (element.hasClass("righttitle")) { editLabel = "右正名"; currentData = this.rightProperTitle; } + else if (element.hasClass("address")) { editLabel = "地址"; currentData = this.address ? [this.address] : []; } $("#current-edit-label").text("目前編輯:" + editLabel); - // 渲染已選 - currentData.forEach((x, index) => { + // 🌟 從物件中取出 fam_name 顯示 + currentData.forEach((item, index) => { + let displayName = typeof item === 'string' ? item : item.fam_name; $("#selectedItem").append(` -
  • ${x}
    - -
  • `); +
  • ${displayName}
    + +
  • `); }); - // 地址不需要出現親友選單 - if (element.hasClass("address")) { - this.bindListEvents(); - return; - } + if (element.hasClass("address")) { this.bindListEvents(); return; } - // 🌟 3. 渲染可選 (嚴格過濾生死狀態) + // 🌟 從已選陣列取出名稱來做比對 + let currentNames = currentData.map(x => typeof x === 'string' ? x : x.fam_name); let filterKeyword = $('#filter_txt').val().trim() || ""; - this.familyMembers.forEach(f => { - // 判斷該親友的生死狀態 (相容 boolean 與字串) - let isDead = (f.deceased === true || f.deceased === "true" || f.deceased === "True"); - // 條件成立:生死符合要求 + 尚未加入 + 關鍵字相符 - if (isDead === requireDeceased && !currentData.includes(f.fam_name) && f.fam_name.includes(filterKeyword)) { + this.familyMembers.forEach(f => { + let isDead = (f.deceased === true || f.deceased === "true" || f.deceased === "True"); + if (isDead === requireDeceased && !currentNames.includes(f.fam_name) && f.fam_name.includes(filterKeyword)) { $("#canselectItem").append(` -
  • - 卍 - ${f.fam_name} - - - ${f.fam_title ? f.fam_title : ""} - - - + -
  • - `); +
  • 卍 ${f.fam_name} + ${f.fam_title ? f.fam_title : ""} + +
  • `); } }); - this.bindListEvents(); }, bindListEvents() { let self = this; - // 🌟 修正1:加上 .off() 解除舊綁定,防止「幽靈疊加點擊」! - // 否則您每點一次,它會因為 renderItemMenu 重新綁定,導致一次加好幾個名字甚至當掉 $("#canselectItem").off("click", ".itemAppend").on("click", ".itemAppend", function (e) { e.preventDefault(); - self.modifyDataAndRerender("add", $(this).siblings(".unselect").text()); + let name = $(this).siblings(".unselect").text(); + let fam = self.familyMembers.find(f => f.fam_name === name); + // 🌟 從親友名單帶入時,組裝成完整物件 + let obj = { + IsShuWen: false, deceased: fam ? (fam.deceased === true || fam.deceased === "True") : false, + fam_gender: "", fam_name: name, fam_file: "", nospace: true, num: fam ? fam.num : 0, option_break: false + }; + self.modifyDataAndRerender("add", obj); }); $("#selectedItem").off("click", ".itemRemove").on("click", ".itemRemove", function (e) { - e.preventDefault(); - self.modifyDataAndRerender("remove", $(this).closest("li").find(".selected").text()); + e.preventDefault(); self.modifyDataAndRerender("remove", $(this).data("idx")); }); - $("#selectedItem").off("click", ".itemUp").on("click", ".itemUp", function (e) { - e.preventDefault(); - self.modifyDataAndRerender("up", $(this).data("idx")); + e.preventDefault(); self.modifyDataAndRerender("up", $(this).data("idx")); }); - $("#selectedItem").off("click", ".itemDown").on("click", ".itemDown", function (e) { - e.preventDefault(); - self.modifyDataAndRerender("down", $(this).data("idx")); + e.preventDefault(); self.modifyDataAndRerender("down", $(this).data("idx")); }); $('#btn_add_custom').off("click").on('click', function (e) { - e.preventDefault(); - let val = $('#custom_txt').val().trim(); - if (val) { self.modifyDataAndRerender("add", val); $('#custom_txt').val(''); } + e.preventDefault(); let val = $('#custom_txt').val().trim(); + if (val) { + // 🌟 手動輸入時,產生帶有 num: 0 的物件 + let obj = { + IsShuWen: false, deceased: false, fam_gender: "", + fam_name: val, fam_file: "", nospace: true, num: 0, option_break: false + }; + self.modifyDataAndRerender("add", obj); + $('#custom_txt').val(''); + } }); $('#filter_txt').off("keyup").on('keyup', () => { @@ -809,17 +776,17 @@ // ========================================== $('#btn_save').off('click').on('click', async function (e) { e.preventDefault(); - //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 - //} + let tablet = { + mid_items: self.properTitle, + left_items: self.alive, + mid_left: self.leftProperTitle, + mid_right: self.rightProperTitle, + addr_items: self.address, + style: self.selectStyle, + } let data = { num: 0, name: $("#template_name_txt").val(), follower_num: VueApp.follower_id, - style_id: $(".styleSelector").val(),tablet:"",shuwen:"" + style_id: $(".styleSelector").val(), tablet: JSON.stringify(tablet), shuwen: "" } console.log("btn_save:", data); }); @@ -830,25 +797,29 @@ let target = this.selectedElement.children().first(), targetArr = null; if (target.hasClass("liveList")) targetArr = this.alive; - else if (target.hasClass("nameList")) targetArr = this.properTitle; + else if (target.hasClass("nameList") || target.hasClass("rosterList") || target.hasClass("ancestor-wrapper")) targetArr = this.properTitle; else if (target.hasClass("lefttitle")) targetArr = this.leftProperTitle; else if (target.hasClass("righttitle")) targetArr = this.rightProperTitle; else if (target.hasClass("address")) { - if (action === "add") this.address = payload; else if (action === "remove") this.address = ""; - targetArr = this.address ? [this.address] : []; + if (action === "add") this.address = typeof payload === 'string' ? payload : payload.fam_name; + else if (action === "remove") this.address = ""; + targetArr = null; } if (targetArr) { - if (action === "add" && !targetArr.includes(payload)) targetArr.push(payload); - else if (action === "add" && targetArr.includes(payload)) { alert("名稱已存在!"); return; } - else if (action === "remove") { let idx = targetArr.indexOf(payload); if (idx > -1) targetArr.splice(idx, 1); } + if (action === "add") { + // 🌟 檢查是否已經存在 (比對物件的 fam_name) + if (targetArr.some(x => x.fam_name === payload.fam_name)) { alert("名稱已存在!"); return; } + targetArr.push(payload); + } + else if (action === "remove") { targetArr.splice(payload, 1); } // 🌟 刪除改用 index else if (action === "up" && payload > 0) [targetArr[payload - 1], targetArr[payload]] = [targetArr[payload], targetArr[payload - 1]]; else if (action === "down" && payload < targetArr.length - 1) [targetArr[payload], targetArr[payload + 1]] = [targetArr[payload + 1], targetArr[payload]]; } this.renderItemMenu(target); this.rerenderPart(); - this.renderEditorRight(); + if (this.renderEditorRight) this.renderEditorRight(); }, rerenderPart() { @@ -873,15 +844,20 @@ if (!d) return; this.selectedElement.empty(); - let html = ""; - // 🌟 重新導向對應的排版引擎 - if (target.hasClass("nameList")) html = this.buildNameList(this.properTitle, d); - else if (target.hasClass("rosterList")) html = this.buildRosterList(this.properTitle, d); - else if (target.hasClass("ancestor-wrapper")) html = this.buildCombinedList(this.properTitle, d); - else if (target.hasClass("liveList")) html = this.buildLiveList(this.alive, d); - else if (target.hasClass("lefttitle")) html = this.buildNormalWithSpace(this.leftProperTitle, d, 'lefttitle'); - else if (target.hasClass("righttitle")) html = this.buildNormalWithSpace(this.rightProperTitle, d, 'righttitle'); + + // 🌟 在傳入排版引擎前,統一 map 成字串陣列 + let pTitleNames = this.properTitle.map(x => typeof x === 'string' ? x : x.fam_name); + let aliveNames = this.alive.map(x => typeof x === 'string' ? x : x.fam_name); + let leftNames = this.leftProperTitle.map(x => typeof x === 'string' ? x : x.fam_name); + let rightNames = this.rightProperTitle.map(x => typeof x === 'string' ? x : x.fam_name); + + if (target.hasClass("nameList")) html = this.buildNameList(pTitleNames, d); + else if (target.hasClass("rosterList")) html = this.buildRosterList(pTitleNames, d); + else if (target.hasClass("ancestor-wrapper")) html = this.buildCombinedList(pTitleNames, d); + else if (target.hasClass("liveList")) html = this.buildLiveList(aliveNames, d); + else if (target.hasClass("lefttitle")) html = this.buildNormalWithSpace(leftNames, d, 'lefttitle'); + else if (target.hasClass("righttitle")) html = this.buildNormalWithSpace(rightNames, d, 'righttitle'); else if (target.hasClass("address")) html = `
    ${this.address}
    `; this.selectedElement.append(html); @@ -981,33 +957,21 @@ renderEditorRight() { let $editorRight = $('#editor-right'); $editorRight.empty(); - - // 1. 設定為直書模式,拔除 flex,恢復自然的區塊(Block)換行特性 $editorRight.css({ - "writing-mode": "vertical-rl", - "text-orientation": "mixed", - "padding": "20px", - "display": "block" // 🌟 關鍵:改用 block,讓每個子元素絕對獨佔一整行 + "writing-mode": "vertical-rl", "text-orientation": "mixed", "padding": "20px", "display": "block" }); - // 2. 收集所有要同步的名單 + // 🌟 同樣從物件萃取 fam_name let syncNames = [ - ...this.properTitle, - ...this.leftProperTitle, - ...this.rightProperTitle + ...this.properTitle.map(x => x.fam_name), + ...this.leftProperTitle.map(x => x.fam_name), + ...this.rightProperTitle.map(x => x.fam_name) ]; - // 3. 渲染名單 syncNames.forEach(name => { let $div = $('
    ').css({ - "font-size": "16pt", - "font-family": "DFKai-sb", - "line-height": "1.2", - "letter-spacing": "2px", - "white-space": "pre-line", - "margin-left": "25px" // 🌟 設定行距 (在直書模式中,左邊就是下一行的位置) + "font-size": "16pt", "font-family": "DFKai-sb", "line-height": "1.2", "letter-spacing": "2px", "white-space": "pre-line", "margin-left": "25px" }).text(name); - $editorRight.append($div); }); },