1438 lines
70 KiB
Plaintext
1438 lines
70 KiB
Plaintext
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="TabletDesign.aspx.cs" Inherits="admin_print_TabletDesign" %>
|
||
|
||
<%@ Register Src="~/admin/_uc/alert.ascx" TagPrefix="uc1" TagName="alert" %>
|
||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
|
||
<style>
|
||
.canvas-area {
|
||
min-height: 100vw !important;
|
||
}
|
||
.limit-text {
|
||
display: inline-block; /* 將 span 轉換為區塊,允許設定寬度 */
|
||
width: 100px; /* 設定可視寬度 */
|
||
overflow: hidden; /* 將超過容器的文字隱藏 */
|
||
white-space: nowrap; /* 強制文字不換行 */
|
||
text-overflow: ellipsis;/* 讓超出部分的文字顯示為省略號 (...) */
|
||
word-break: break-all;
|
||
overflow-wrap: break-word;
|
||
font-weight:900;
|
||
font-size:large;
|
||
}
|
||
.custom-menu {
|
||
position: fixed; /* 關鍵:使用 fixed 定位 */
|
||
background: white;
|
||
border: 1px solid #ccc;
|
||
list-style: none;
|
||
padding: 5px 0;
|
||
box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
|
||
z-index: 999;
|
||
}
|
||
|
||
</style>
|
||
</asp:Content>
|
||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" runat="Server">
|
||
</asp:Content>
|
||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
|
||
<div id="content" class="container-lg">
|
||
|
||
<v-tabs
|
||
v-model="tab"
|
||
background-color="primary"
|
||
centered
|
||
dark
|
||
icons-and-text>
|
||
<v-tabs-slider></v-tabs-slider>
|
||
|
||
<v-tab href="#版型">
|
||
版型
|
||
<v-icon>mdi-book-open-outline</v-icon>
|
||
</v-tab>
|
||
|
||
<v-tab href="#內容尺寸">
|
||
內容尺寸
|
||
<v-icon>mdi-bowl-mix-outline</v-icon>
|
||
</v-tab>
|
||
|
||
<v-tab href="#列印尺寸">
|
||
列印尺寸
|
||
<v-icon>mdi-dome-light</v-icon>
|
||
</v-tab>
|
||
<v-tab href="#列印模式">
|
||
列印模式
|
||
<v-icon>mdi-weather-cloudy</v-icon>
|
||
</v-tab>
|
||
<v-tab href="#底圖">
|
||
底圖
|
||
<v-icon>mdi-account-circle-outline</v-icon>
|
||
</v-tab>
|
||
<v-tab href="#牌位顯示項目">
|
||
牌位顯示項目
|
||
<v-icon>mdi-flower</v-icon>
|
||
</v-tab>
|
||
<v-tab href="#存檔">
|
||
存檔
|
||
<v-icon>mdi-flower</v-icon>
|
||
</v-tab>
|
||
</v-tabs>
|
||
|
||
<v-tabs-items v-model="tab">
|
||
<v-tab-item
|
||
value="版型">
|
||
<v-card >
|
||
<v-card-text>
|
||
<!-- 使用 v-tabs 並設定 fixed-tabs 或 center-active -->
|
||
<v-row>
|
||
<v-col cols="12" class="pa-0">
|
||
<v-tabs
|
||
class="fixed-bottom-tabs w-100"
|
||
bg-color="surface"
|
||
color="primary"
|
||
align-tabs="start"
|
||
center-active
|
||
show-arrows
|
||
v-resize="onTabsResize"
|
||
>
|
||
<v-tab v-for="item in allStyle" :key="item.styleID" :value="item.styleID" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="changeStyle(item.styleID)"
|
||
@contextmenu.prevent="openStyleMenu($event,item.styleID)">
|
||
<span class=" limit-text mt-1">{{ item.name }}</span></button>
|
||
|
||
</v-tab>
|
||
|
||
</v-tabs>
|
||
</v-col>
|
||
</v-row>
|
||
<v-menu
|
||
absolute
|
||
offset-y
|
||
:position-x="styleMenuX"
|
||
:position-y="styleMenuY"
|
||
v-model="showStyleMenu"
|
||
>
|
||
<v-list>
|
||
<v-list-item
|
||
:key="showStyleMenu">
|
||
<v-list-item-title>
|
||
<button class="btn btn-primary" @click.prevent="removeStyle()">刪除</button>
|
||
</v-list-item-title>
|
||
</v-list-item>
|
||
</v-list>
|
||
</v-menu>
|
||
|
||
<%--<ul class="dropdown-menu style-menu">
|
||
</ul>--%>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-tab-item>
|
||
<v-tab-item
|
||
value="內容尺寸">
|
||
<v-card flat>
|
||
<v-card-text>
|
||
|
||
<div class="form-floating mb-3">
|
||
<v-row>
|
||
<v-col cols="12" class="pa-0">
|
||
<v-tabs
|
||
class="fixed-bottom-tabs w-100"
|
||
bg-color="surface"
|
||
color="primary"
|
||
align-tabs="start"
|
||
center-active
|
||
>
|
||
<v-tab v-for="item in allSize" :key="item.id" :value="item.id" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="changePaper1(item)"><span class=" limit-text mt-1">{{ item.name }}</span></button>
|
||
</v-tab>
|
||
</v-tabs>
|
||
</v-col>
|
||
</v-row>
|
||
</div>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-tab-item>
|
||
<v-tab-item
|
||
value="列印尺寸">
|
||
<v-card flat>
|
||
<v-card-text>
|
||
<v-row>
|
||
<v-col cols="12" class="pa-0">
|
||
<v-tabs
|
||
class="fixed-bottom-tabs w-100"
|
||
bg-color="surface"
|
||
color="primary"
|
||
align-tabs="start"
|
||
center-active
|
||
>
|
||
<v-tab v-for="item in allSize" :key="item.id" :value="item.id" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="changePrintSize(item.id)"><span class=" limit-text mt-1">{{ item.name }}</span></button>
|
||
</v-tab>
|
||
</v-tabs>
|
||
</v-col>
|
||
</v-row>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-tab-item>
|
||
<v-tab-item
|
||
value="列印模式">
|
||
<v-card flat>
|
||
<v-card-text>
|
||
<v-tabs
|
||
class="fixed-bottom-tabs w-100"
|
||
bg-color="surface"
|
||
color="primary"
|
||
align-tabs="start"
|
||
center-active
|
||
>
|
||
<v-tab key="combine" value="combine" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="changePrintMode('combine')"><span class=" limit-text mt-1">合併</span></button>
|
||
</v-tab>
|
||
<v-tab key="origin" value="origin" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="changePrintMode('origin')"><span class=" limit-text mt-1">原尺寸</span></button>
|
||
</v-tab>
|
||
</v-tabs>
|
||
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-tab-item>
|
||
<v-tab-item
|
||
value="底圖">
|
||
<v-card flat>
|
||
<v-card-text>
|
||
<v-tabs
|
||
class="fixed-bottom-tabs w-100"
|
||
bg-color="surface"
|
||
color="primary"
|
||
align-tabs="start"
|
||
center-active
|
||
>
|
||
<v-tab v-for="item in bg" :key="item.name" :value="item.name" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="changeBg(item)"><span class=" limit-text mt-1">{{ item.name }}</span></button>
|
||
</v-tab>
|
||
</v-tabs>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-tab-item>
|
||
<v-tab-item
|
||
value="牌位顯示項目">
|
||
<v-card flat>
|
||
<v-card-text>
|
||
<v-tabs
|
||
class="fixed-bottom-tabs w-100"
|
||
bg-color="surface"
|
||
color="primary"
|
||
align-tabs="start"
|
||
center-active
|
||
>
|
||
<v-tab key="address" value="address" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="displayItem('address')"><span class=" limit-text mt-1">地址欄</span></button>
|
||
</v-tab>
|
||
<v-tab key="title1" value="title1" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="displayItem('title1')"><span class=" limit-text mt-1">牌位正名</span></button>
|
||
</v-tab>
|
||
<v-tab key="lefttitle" value="lefttitle" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="displayItem('lefttitle')"><span class=" limit-text mt-1">左正名</span></button>
|
||
</v-tab>
|
||
<v-tab key="righttitle" value="righttitle" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="displayItem('righttitle')"><span class=" limit-text mt-1">右正名</span></button>
|
||
</v-tab>
|
||
<v-tab key="titletriangle" value="titletriangle" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="displayItem('titletriangle')"><span class=" limit-text mt-1">品字名單</span></button>
|
||
</v-tab>
|
||
<v-tab key="combined" value="combined" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="displayItem('combined')"><span class=" limit-text mt-1">雙姓合併置中</span></button>
|
||
</v-tab>
|
||
<v-tab key="alive" value="alive" class="d-flex flex-column py-2">
|
||
<button tytpe="button" class="btn btn-info" @click.prevent="displayItem('alive')"><span class=" limit-text mt-1">陽上報恩</span></button>
|
||
</v-tab>
|
||
</v-tabs>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-tab-item>
|
||
<v-tab-item
|
||
value="存檔">
|
||
<v-card flat>
|
||
<v-card-text>
|
||
<span class="btn btn-sm btn-outline-info w-100 mb-2" onclick="saveStyle()">存檔</span>
|
||
</v-card-text>
|
||
</v-card>
|
||
</v-tab-item>
|
||
</v-tabs-items>
|
||
</div>
|
||
<div class="d-flex overflow-hidden" style="height: 1920px">
|
||
<div class="p-2 text-white floting-box" style="width: 250px; left: 20px; top: 80px;">
|
||
<%-- <h6 class="border-bottom pb-2">設計工具箱</h6>--%>
|
||
<div class="input-group mb-3">
|
||
<v-list flat disabled style="width: 250px;">
|
||
<v-subheader>型版屬性</v-subheader>
|
||
<v-list-item-group color="primary">
|
||
<v-list-item >
|
||
<v-list-item-content>
|
||
<v-list-item-title>型版名稱:</v-list-item-title>
|
||
<input type="text" v-model="styleName" />
|
||
</v-list-item-content>
|
||
</v-list-item>
|
||
<v-list-item >
|
||
<v-list-item-content>
|
||
<v-list-item-title>內容尺寸:{{getParseName(paperSize,"content")}}</v-list-item-title>
|
||
</v-list-item-content>
|
||
</v-list-item>
|
||
<v-list-item >
|
||
<v-list-item-content>
|
||
<v-list-item-title >列印尺寸:{{ getParseName(printSize,"print") }}</v-list-item-title>
|
||
</v-list-item-content>
|
||
</v-list-item>
|
||
<v-list-item >
|
||
<v-list-item-content>
|
||
<v-list-item-title >列印模式:{{ getParseName(printMode,"mode") }}</v-list-item-title>
|
||
</v-list-item-content>
|
||
</v-list-item>
|
||
<v-list-item >
|
||
<v-list-item-content>
|
||
<v-list-item-title> 底圖:{{ backendInp }}</v-list-item-title>
|
||
</v-list-item-content>
|
||
</v-list-item>
|
||
</v-list-item-group>
|
||
</v-list>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="printArea" style="width: 100vw; height: 100vh">
|
||
<div class="canvas-area flex-grow-1 overflow-auto d-flex flex-column align-items-center position-relative" id="canvas">
|
||
<div class="tablet-paper">
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="attr-box" class="d-none p-2 bg-dark text-whit floting-box" style="top: 80px; right: 20px">
|
||
<div class="form-floating mb-3">
|
||
<textarea id="inp-text" class="form-control form-control-sm mb-2" rows="5"></textarea>
|
||
<label class="small" for="inp-text">內容文字</label>
|
||
</div>
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="inp-size" class="form-control form-control-sm mb-2">
|
||
<label class="small" for="inp-size">字體大小 (pt)</label>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="xPosition" class="form-control form-control-sm mb-2" onchange="changePosition()">
|
||
<label class="small" for="xPosition">X</label>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="yPosition" class="form-control form-control-sm mb-2" onchange="changePosition()">
|
||
<label class="small" for="yPosition">Y</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="width" class="form-control form-control-sm mb-2">
|
||
<label class="small" for="width">寬度</label>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="height" class="form-control form-control-sm mb-2">
|
||
<label class="small" for="height">高度</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="textWidth" class="form-control form-control-sm mb-2">
|
||
<label class="small" for="textWidth">文字寬度</label>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="textHeight" class="form-control form-control-sm mb-2">
|
||
<label class="small" for="textHeight">文字高度</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-6">
|
||
<div class="form-floating mb-3">
|
||
<input type="number" id="textSpan" class="form-control form-control-sm mb-2">
|
||
<label class="small" for="textSpan">文字間距</label>
|
||
</div>
|
||
</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">
|
||
<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>
|
||
|
||
<v-snackbar
|
||
v-model="snackbar.show"
|
||
timeout="2000">
|
||
{{ snackbar.text }}
|
||
<template v-slot:action="{ attrs }">
|
||
<v-btn
|
||
text
|
||
v-bind="attrs"
|
||
@click="snackbar.show = false"
|
||
>
|
||
關閉
|
||
</v-btn>
|
||
</template>
|
||
</v-snackbar>
|
||
</asp:Content>
|
||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" runat="Server">
|
||
</asp:Content>
|
||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" runat="Server">
|
||
<script>
|
||
let VueApp = new Vue({
|
||
el: '#app',
|
||
vuetify: new Vuetify(vuetify_options),
|
||
data() {
|
||
return {
|
||
snackbar: {
|
||
show: false,
|
||
text: "",
|
||
},
|
||
tab: "型版",
|
||
value:"",
|
||
elements: [],
|
||
activeId: null,
|
||
styleID: null,
|
||
paper: { width: "100", height: "272" },
|
||
rosterLimit: 10,
|
||
allSize: [
|
||
],
|
||
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" }
|
||
],
|
||
allStyle: [
|
||
],
|
||
tabletElements: [
|
||
],
|
||
orientation: "portrait",
|
||
toast: null,
|
||
paperSize: "",
|
||
backendInp: "",
|
||
printSize: "",
|
||
printMode: "",
|
||
perpage: "",
|
||
//rosterLimit: "",
|
||
newPaper: {
|
||
paperName: "",
|
||
paperWidth: "",
|
||
paperHeight:""
|
||
},
|
||
styleName: "",
|
||
showStyleMenu: false,
|
||
styleMenuX: 0,
|
||
styleMenuY: 0,
|
||
styleMenuStyle:""
|
||
}
|
||
},
|
||
computed: {
|
||
|
||
},
|
||
mounted() {
|
||
this.init()
|
||
},
|
||
methods: {
|
||
onTabsResize(e) {
|
||
console.log(e)
|
||
// 當畫面變動時,呼叫 Vuetify 內部的呼叫方法重新計算寬度
|
||
//if (this.$refs.myTabs) {
|
||
// this.$refs.myTabs.onResize();
|
||
//}
|
||
},
|
||
getParseName(id, classes) {
|
||
if (id != null) {
|
||
if (classes === "style") {
|
||
|
||
let data = this.allStyle.find(x => x.styleID === id)
|
||
|
||
return data?.name
|
||
} else if (classes === "content") {
|
||
let data = this.allSize.find(x => x.id === id)
|
||
return data?.name
|
||
} else if (classes === "print") {
|
||
let data = this.allSize.find(x => x.id === id)
|
||
return data?.name
|
||
} else if (classes === "mode") {
|
||
return id === "origin" ? "原尺寸" : "合併";
|
||
}
|
||
}
|
||
return ""
|
||
},
|
||
init() {
|
||
//$(".tablet-element").draggable({});
|
||
this.getPaperSize();
|
||
this.getElements();
|
||
this.getStyles();
|
||
this.bindEvents();
|
||
this.bindBackend();
|
||
|
||
this.bg.forEach(x => {
|
||
$("#backendInp").append("<option value='" + x.name + "'>" + x.name + "</option>")
|
||
});
|
||
//$("#paperOrientation").html("<span>直向</span>")
|
||
this.toast = new bootstrap.Toast($('#liveToast'));
|
||
},
|
||
openStyleMenu(e, id) {
|
||
this.showStyleMenu = true;
|
||
this.styleMenuX = e.clientX;
|
||
this.styleMenuY = e.clientY;
|
||
this.styleMenuStyle = `top:${e.clientY}px,left:${e.clientX}px`
|
||
this.styleID=id
|
||
|
||
},
|
||
closeStyleMenu() {
|
||
this.showStyleMenu = false;
|
||
},
|
||
print() {
|
||
let self = this;
|
||
let s = this.allStyle.find(x => x.styleID == this.styleID)
|
||
let size = this.allSize.find(x => x.id = s.paperSize)
|
||
let w = window.open('', '_blank');
|
||
if (!w) {
|
||
|
||
$("#toast_body").html(`${"請允許瀏覽器開啟彈出式視窗!"}`)
|
||
self.toast.show();
|
||
return;
|
||
}
|
||
|
||
// 1. 抓取您原本網頁定義的 HTTP_HOST (您的全域變數),如果沒有則抓取當前網址
|
||
let hostUrl = typeof HTTP_HOST !== 'undefined' ? 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>
|
||
|
||
/* 名單金字塔佈局容器 */
|
||
.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: ${style.fontFamily};
|
||
/* 確保名字本身不會佔據過多寬度導致間距看起來很大 */
|
||
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: flex-start;
|
||
// 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
|
||
}
|
||
|
||
/* 列印時的紙張大小與頁面歸零 */
|
||
@media print {
|
||
@page {
|
||
/* 動態套用您設定的紙張寬高 */
|
||
size: ${size.width}mm ${size.height}mm portrait;
|
||
margin: 0;
|
||
}
|
||
body, html {
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
|
||
}
|
||
body{
|
||
--page-w: ${size.width}mm;
|
||
--page-h: ${size.height}mm;
|
||
--font-max: 22pt;
|
||
--bg-padding: 0;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
${canvasHtml}
|
||
</body>
|
||
</html>
|
||
`;
|
||
|
||
w.document.write(htmlContent);
|
||
w.document.close();
|
||
|
||
// 5. 將等待時間稍微拉長至 1.2 秒,確保大張的 SVG 底圖與外部字體(如標楷體)下載完畢
|
||
setTimeout(() => {
|
||
w.focus();
|
||
w.print();
|
||
}, 1200);
|
||
},
|
||
changeOrientation() {
|
||
this.orientation = (this.orientation === "portrait" ? "land" : "portrait");
|
||
$("#paperOrientation").empty();
|
||
let ori = this.orientation == "portrait" ? "直向" : "橫向";
|
||
$("#paperOrientation").html("<span>" + ori + "</span>");
|
||
let paperSize = $("#paperSize").val()
|
||
|
||
let size = this.allSize.find(x => x.name == paperSize)
|
||
this.paper.width = this.orientation == "portrait" ? size.width : size.height;
|
||
this.paper.height = this.orientation == "portrait" ? size.height : size.width;
|
||
$(".tablet-paper").css({
|
||
"background-color": "white",
|
||
width: this.paper.width + "mm",
|
||
height: this.paper.height + "mm",
|
||
"background-repeat": "no-repeat!important",
|
||
});
|
||
},
|
||
async getPaperSize() {
|
||
let self = this
|
||
self.allSize.length = 0;
|
||
$("#paperSize").html("")
|
||
$("#printSize").html("")
|
||
await axios
|
||
.post(HTTP_HOST + 'api/tablet/GetPaperSize', {})
|
||
.then(response => {
|
||
//if (response.result=="Y") {
|
||
if (response.status == "200") {
|
||
let data = response.data;
|
||
if (data.result == "Y") {
|
||
data.data.forEach(x => {
|
||
self.allSize.push({ name: x.paperName, id: x.paperID, width: x.width, height: x.height })
|
||
});
|
||
}
|
||
}
|
||
});
|
||
$("#paperSize").append("<option value='newsize' >新增尺寸</option>")
|
||
this.allSize.forEach(x => {
|
||
$("#paperSize").append("<option value='" + x.id + "'>" + x.name + "(" + x.width + "*" + x.height + ")</option>")
|
||
$("#printSize").append("<option value='" + x.id + "'>" + x.name + "(" + x.width + "*" + x.height + ")</option>");
|
||
})
|
||
},
|
||
async getElements() {
|
||
let self = this
|
||
await axios
|
||
.post(HTTP_HOST + 'api/tablet/GetTabletElement', {})
|
||
.then(response => {
|
||
console.log(response)
|
||
//if (response.result=="Y") {
|
||
if (response.status == "200") {
|
||
let data = response.data;
|
||
console.log("data:", data)
|
||
if (data.result == "Y") {
|
||
data.data.forEach(x => {
|
||
self.tabletElements.push(x);
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
async getStyles() {
|
||
let self = this
|
||
self.allSize.length = 0
|
||
$(".style-menu").html("")
|
||
await axios
|
||
.post(HTTP_HOST + 'api/tablet/GetStyleData', {})
|
||
.then(response => {
|
||
//if (response.result=="Y") {
|
||
if (response.status == "200") {
|
||
let data = response.data;
|
||
if (data.result == "Y") {
|
||
data.data.forEach(x => {
|
||
self.allStyle.push(x);
|
||
$(".style-menu").append("<li><span class='dropdown-item style-item' data-value='" + x.styleID + "'>" + x.name + "</span></li>");
|
||
});
|
||
|
||
this.bindDropdown();
|
||
}
|
||
}
|
||
//}
|
||
});
|
||
},
|
||
bindDropdown() {
|
||
let self = this;
|
||
$(".style-menu li").on("click", function (e) {
|
||
$(".style-menu li").removeClass('active');
|
||
$(this).addClass('active');
|
||
$("#styleName").val(e.target.textContent);
|
||
self.styleID = e.target.getAttribute("data-value")
|
||
self.changeStyle(e.target.getAttribute("data-value"));
|
||
let style = self.allStyle.find(x => x.styleID == e.target.getAttribute("data-value"));
|
||
if (style) {
|
||
let size = self.allSize.find(x => x.id == style.paperSize);
|
||
if (size) {
|
||
self.paper.width = size.width;
|
||
self.paper.height = size.height;
|
||
} else {
|
||
self.paper.width = 100;
|
||
self.paper.height = 272;
|
||
}
|
||
} else {
|
||
self.paper.width = 100;
|
||
self.paper.height = 272;
|
||
}
|
||
|
||
});
|
||
},
|
||
async changeStyle(id) {//切換版型,抓回明細
|
||
let self = this;
|
||
this.elements.length = 0;
|
||
let obj = this.elements
|
||
let s = this.allStyle.find(x => x.styleID == id)
|
||
let size = this.allSize.find(x => x.id == s.paperSize)
|
||
//this.showStyleMenu = true;
|
||
this.paper.width = size.width;
|
||
this.paper.height = size.height;
|
||
this.rosterLimit = s.rosterLimit;
|
||
this.paperSize = s.paperSize
|
||
this.backendInp = s.backendImg
|
||
this.printSize = s.printSize
|
||
this.printMode = s.printMode
|
||
this.perpage = s.perpage
|
||
this.styleID = id
|
||
|
||
//this.rosterLimit = s.rosterLimit
|
||
//$("#paperSize").val(s.paperSize);
|
||
//$("#backendInp").val(s.backendImg);
|
||
//$("#printSize").val(s.printSize);
|
||
//$("#printMode").val(s.printMode);
|
||
//$("#perpage").val(s.printPageCount);
|
||
//$("#rosterLimit").val(s.rosterLimit);
|
||
let img = this.bg.find(x => x.name == s.backendImg);
|
||
$(".tablet-paper").css({
|
||
'background-image': 'url(' + (img ? img.path : "") + ')', 'background-size': '100% 100%',
|
||
width: (size ? size.width : self.paper.width) + "mm",
|
||
height: (size ? size.height : self.paper.height) + "mm",
|
||
})
|
||
await axios
|
||
.post(HTTP_HOST + 'api/tablet/GetStyleDetailData', { styleID: id })
|
||
.then(response => {
|
||
if (response.status == "200") {
|
||
let data = response.data;
|
||
if (data.result == "Y") {
|
||
let details = data.data;
|
||
$(".tablet-paper").empty()
|
||
details.forEach(el => {
|
||
|
||
let te = self.tabletElements.find(x => {
|
||
return x.elementID == el.elementID
|
||
});
|
||
let config = {
|
||
id: el.elementID,
|
||
type: te.elementType,
|
||
text: te.sampleContent.replaceAll("\\n", "\n"),
|
||
x: el.startX, y: el.startY,
|
||
style: {
|
||
fontSize: el.fontSize,
|
||
fontFamily: el.fontFamily,
|
||
isVertical: true,
|
||
letterSpacing: 5, lineHeight: 1.5,
|
||
visibility: el.isActive,
|
||
},
|
||
|
||
width: el.width,
|
||
height: el.height,
|
||
textWidth: el.textWidth,
|
||
textHeight: el.textHeight,
|
||
breakLen: el.breakLen,
|
||
backendInp: el.backendImg,
|
||
textSpan: el.textSpan
|
||
};
|
||
|
||
obj.push(config)
|
||
|
||
})
|
||
|
||
this.render();
|
||
}
|
||
}
|
||
});
|
||
},
|
||
changeBg(item) {
|
||
this.backendInp=item.name
|
||
let path = this.bg.find(el => el.name == item.name)//$("#backendInp").val()
|
||
$(".tablet-paper").css({ 'background-image': 'url(' + path.path + ')', 'background-size': '100% 100%' })
|
||
},
|
||
changePrintSize(id) {
|
||
this.printSize=id
|
||
},
|
||
changePosition() {
|
||
//this.activeId = id;
|
||
const el = this.elements.find(x => x.id === this.activeId);
|
||
el.x = $('#xPosition').val();
|
||
el.y = $("#yPosition").val();
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({ left: el.x + "mm", top: el.y + "mm" });
|
||
},
|
||
changePaper1(val) {
|
||
//$("#paperSize").val(val)
|
||
this.paperSize = val.id
|
||
console.log("changePaper1:",val)
|
||
this.changePaper()
|
||
},
|
||
changePaper() {
|
||
let paperSize = this.paperSize//$("#paperSize").val()
|
||
let self = this;
|
||
if (paperSize == "newsize") {
|
||
$("#sizeDiv").attr("style", "display:");
|
||
} else {
|
||
// console.log("paperSize:", paperSize);
|
||
$("#sizeDiv").attr("style", "display:none;");
|
||
let size = this.allSize.find(x => x.id == paperSize)
|
||
$(".tablet-paper").css({
|
||
"background-color": "white",
|
||
width: (size ? size.width : self.paper.width) + "mm",
|
||
height: (size ? size.height : self.paper.height) + "mm",
|
||
});
|
||
}
|
||
},
|
||
async removePaper() {
|
||
let self = this;
|
||
let paperSize =this.paperSize //$("#paperSize").val()
|
||
//console.log("paperSize:",paperSize);
|
||
if (paperSize == "newsize" || paperSize == "" || paperSize == null || paperSize == undefined) {
|
||
|
||
$("#toast_body").html("請選擇要刪除的尺寸")
|
||
self.toast.show();
|
||
return false
|
||
}
|
||
let ps = { paperSize: paperSize }
|
||
await axios
|
||
.post(HTTP_HOST + 'api/tablet/DelPaperSize', ps)
|
||
.then(response => {
|
||
if (response.status == "200") {
|
||
//if (response.data.result == "Y") {
|
||
|
||
$("#toast_body").html(`${response.data.message}`)
|
||
self.toast.show();
|
||
//}
|
||
if (response.data.result == "Y") {
|
||
|
||
self.getPaperSize()
|
||
//$("#paperSize").val("")
|
||
this.paperSize=""
|
||
this.changePaper()
|
||
}
|
||
}
|
||
});
|
||
},
|
||
async removeStyle() {
|
||
let self = this;
|
||
let sID = this.styleID
|
||
//console.log("styleID:", sID);
|
||
if (sID == "000001" || sID == null || sID == undefined) {
|
||
|
||
//$("#toast_body").html("請選擇要刪除的版型")
|
||
//self.toast.show();
|
||
this.snackbar.text = "請選擇要刪除的版型"
|
||
this.snackbar.show=true
|
||
return false
|
||
}
|
||
let ps = { styleID: sID }
|
||
await axios
|
||
.post(HTTP_HOST + 'api/tablet/DelTabletStyle', ps)
|
||
.then(response => {
|
||
if (response.status == "200") {
|
||
//if (response.data.result == "Y") {
|
||
|
||
$("#toast_body").html(`${response.data.message}`)
|
||
self.toast.show();
|
||
//}
|
||
if (response.data.result == "Y") {
|
||
self.getStyles()
|
||
}
|
||
}
|
||
});
|
||
},
|
||
async savePaperSize() {
|
||
let ps = {
|
||
paperName: this.newPaper.paperName, //$("#paperName").val(),
|
||
width: this.newPaper.paperWidth,//$("#paperWidth").val(),
|
||
height: this.newPaper.paperHeight//$("#paperHeight").val()
|
||
}
|
||
let self = this;
|
||
//console.log(ps);
|
||
await axios
|
||
.post(HTTP_HOST + 'api/tablet/SavePaperSize', ps)
|
||
.then(response => {
|
||
if (response.status == "200") {
|
||
if (response.data.result == "Y") {
|
||
|
||
$("#toast_body").html(`${"ok"}`)
|
||
self.toast.show();
|
||
self.getPaperSize()
|
||
//$("#paperSize").val("")
|
||
self.paperSize=""
|
||
self.changePaper()
|
||
}
|
||
}
|
||
});
|
||
|
||
},
|
||
changeRosterLimit() {
|
||
//this.rosterLimit = parseInt(this.rosterLimit) //parseInt($("#rosterLimit").val());
|
||
},
|
||
changePrintMode() {
|
||
|
||
},
|
||
bindEvents() {
|
||
// 文字變更連動渲染
|
||
//$('#rosterLimit').on('input', (e) =>this.)
|
||
//console.log("OOQ");
|
||
$('#inp-text').on('input', (e) => this.updateActive('text', $(e.target).val()));
|
||
$('#inp-size').on('input', (e) => this.updateActive('fontSize', $(e.target).val()));
|
||
$('#width').on('input', (e) => this.updateActive('width', $(e.target).val()))
|
||
$('#2offset').on('input', (e) => this.updateActive('2offset', $(e.target).val()))
|
||
$('#3offset').on('input', (e) => this.updateActive('3offset', $(e.target).val()))
|
||
$('#4offset').on('input', (e) => this.updateActive('4offset', $(e.target).val()))
|
||
$('#breakLen').on('input', (e) => this.updateActive('breakLen', $(e.target).val()))
|
||
$('#width').on('input', (e) => this.updateActive('width', $(e.target).val()))
|
||
$('#height').on('input', (e) => this.updateActive('height', $(e.target).val()))
|
||
$('#textWidth').on('input', (e) => this.updateActive('textWidth', $(e.target).val()))
|
||
$('#textHeight').on('input', (e) => this.updateActive('textHeight', $(e.target).val()))
|
||
$('#textSpan').on('input', (e) => this.updateActive('textSpan', $(e.target).val()))
|
||
$(document).on('mousedown', '.tablet-element', (e) => {
|
||
e.stopPropagation();
|
||
this.select($(e.currentTarget).attr('id'));
|
||
});
|
||
},
|
||
loadConfig() {
|
||
$.ajax({
|
||
type: "POST", url: "TabletDesigner.aspx/GetConfig", contentType: "application/json",
|
||
success: (res) => {
|
||
const data = JSON.parse(res.d);
|
||
this.elements = data.elements;
|
||
this.render();
|
||
}
|
||
});
|
||
},
|
||
// 處理分頁渲染邏輯
|
||
render() {
|
||
$(".tablet-paper").empty();
|
||
//const $canvas = $('#canvas').empty();
|
||
|
||
//const roster = this.elements.find(e => e.type === 'roster');
|
||
|
||
let style = this.allStyle.find(x => x.styleID == this.styleID);
|
||
let size = this.allSize.find(x => x.id == style.paperSize);
|
||
//console.log(this.paper, size);
|
||
//for (let p = 0; p < pages; p++) {
|
||
// const $paper = $('<div class="tablet-paper"></div>')
|
||
// .css({ "background-color": "red", width: this.paper.width + 'mm', height: this.paper.height + 'mm' });
|
||
let $paper = $(".tablet-paper").css({
|
||
"background-color": "white",
|
||
width: (size ? size.width : this.paper.width) + 'mm',
|
||
height: (size ? size.height : this.paper.height) + 'mm',
|
||
position: "absolute"
|
||
});
|
||
//const slice = names.slice(p * this.rosterLimit, (p + 1) * this.rosterLimit);
|
||
//const roster = this.elements.find(e => e.id === this.styleID);
|
||
let self = this;
|
||
|
||
this.elements.forEach(el => {
|
||
const names = el.text.split('\n').filter(s => s.trim());
|
||
const pages = Math.max(1, Math.ceil(names.length / self.rosterLimit));
|
||
const slice = names.slice(0 * self.rosterLimit, (0 + 1) * self.rosterLimit);
|
||
$paper.append(self.createEl(el, slice));
|
||
});
|
||
//$canvas.append($paper);
|
||
//}
|
||
|
||
//let s = this.allStyle.find(x => x.styleID == this.styleID);
|
||
//let d = this.elements.filter(x => x.styleID == this.styleID);
|
||
//console.log(s,d);
|
||
this.makeDraggable();
|
||
},
|
||
|
||
createEl(el, slice) {
|
||
let html = '';
|
||
// 1. 智慧名單渲染
|
||
if (el.type === 'roster') {
|
||
html = this.renderRoster(slice, el);
|
||
}
|
||
// 2. 正名合併置中
|
||
else if (el.type === 'combined-center') {
|
||
const parts = el.text.split('\n');
|
||
html = `<div class="ancestor-wrapper" style="width:${el.width}px !important;height:${el.height}px !important;" >
|
||
<span class="main-name" >${parts[0] || ''}</span>
|
||
<span class="sub-text">${parts[1] || ''}</span>
|
||
</div>`;
|
||
}
|
||
// 3. 地址數字轉橫排
|
||
else if (el.id === 'address') {
|
||
html = el.text.replace(/(\d+)/g, '<span class="tate-chu-yoko">$1</span>');
|
||
}
|
||
else if (el.id === 'title1') {
|
||
html = this.renderNameList(slice, el);
|
||
}
|
||
else if (el.id === 'alive') {
|
||
console.log("createEl:", slice, el)
|
||
html = this.renderLiveList(slice, el);
|
||
}
|
||
else {
|
||
html = el.text;
|
||
}
|
||
|
||
return $(`<div class="tablet-element vertical-text ${this.activeId === el.id ? 'selected' : ''}" id="${el.id}"></div>`)
|
||
.css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt', fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility
|
||
//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);
|
||
},
|
||
renderNameList(names, el) {
|
||
let $namelist = $(`<div class='nameList'></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": `${el.textSpan}px`,
|
||
"column-gap": `${el.textSpan}px`,
|
||
"align-items": "center",
|
||
});
|
||
//
|
||
//"row-gap": "1px",
|
||
let self = this;
|
||
names.forEach(n => {
|
||
$namelist.append(self.renderNameSpan(n, el))
|
||
})
|
||
return $namelist;
|
||
},
|
||
renderNameSpan(name, el) {
|
||
return $(`<span>${name}</span>`).css({
|
||
display: "block",
|
||
"min-height": `${el.textHeight}px`,
|
||
"max-height": `${el.height}px`,
|
||
width: `${el.textWidth}px`,
|
||
"text-align": "justify",
|
||
"text-align-last": "justify",
|
||
|
||
"margin-left": "5px",
|
||
"text-justify": "inter-character",
|
||
});// "margin-bottom": `${el.textSpan}px`,
|
||
},
|
||
renderLiveList(names, el) {
|
||
console.log("renderLiveList:", names);
|
||
//$("#imp-text").updateActive("text",names)
|
||
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": "BiauKai",
|
||
//"letter-spacing": `${el.textSpan}px`,
|
||
"column-gap": `${el.textSpan}px`,
|
||
"align-items": "center",
|
||
});
|
||
// "column-gap": `${el.textSpan}px`,
|
||
//"row-gap": "1px",
|
||
let self = this;
|
||
names.forEach(n => {
|
||
$namelist.append(self.renderLiveSpan(n, el))
|
||
})
|
||
return $namelist;
|
||
},
|
||
renderLiveSpan(name, el) {
|
||
console.log("renderLiveSpan:", name, el
|
||
);
|
||
return $(`<span>${name}</span>`).css({
|
||
display: "block",
|
||
"min-height": `${el.textHeight}px`,
|
||
"max-height": `${el.height}px`,
|
||
width: `${el.textWidth}px`,
|
||
"text-align": "justify",
|
||
"text-align-last": "justify",
|
||
|
||
"margin-left": "5px",
|
||
"text-justify": "inter-character",
|
||
});//"margin-bottom": `${el.textSpan}px`,
|
||
},
|
||
// 品字佈局邏輯:一上二下
|
||
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=""></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: `${el.textSpan}px`,
|
||
});
|
||
h.append(this.renderNameGroups(top, el));
|
||
if (bot.length) h.append(this.renderNameGroups(bot, el));
|
||
return h
|
||
},
|
||
renderNameGroups(items, el) {
|
||
let g = $(`<div ></div>`).css({
|
||
"display": "flex",
|
||
"flex-direction": "column",
|
||
"justify-content": "center",
|
||
"height": el.textHeight,
|
||
})
|
||
items.forEach(x => {
|
||
g.append($(`<div class="" style="font-size:${el.style.fontSize}pt;letter-spacing: 10px;text-align: justify; text-align-last: justify;">${x}</div>`))
|
||
})
|
||
return g;
|
||
},
|
||
|
||
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;
|
||
},
|
||
getPosInMm(px) {
|
||
return parseFloat((px * 25.4 / 96).toFixed(2));
|
||
},
|
||
// 拖動後同步所有頁面的位置
|
||
makeDraggable() {
|
||
const self = this;
|
||
$(".tablet-element").draggable({
|
||
start(e, ui) {
|
||
console.log("gogogogo");
|
||
},
|
||
stop(e, ui) {
|
||
const id = $(this).attr('id');
|
||
const el = self.elements.find(x => x.id === id);
|
||
el.x = self.getPosInMm(ui.position.left);
|
||
el.y = self.getPosInMm(ui.position.top);
|
||
//console.log(id,ui.position.left, ui.position.top,ui.offset.left,ui.offset.top);
|
||
$(`.tablet-element[id="${id}"]`).css({ left: el.startX + "mm", top: el.startY + "mm" });
|
||
}
|
||
});
|
||
},
|
||
|
||
select(id) {
|
||
console.log("QQ:", id);
|
||
this.activeId = id;
|
||
console.log("QQ:", this.elements);
|
||
const el = this.elements.find(x => x.id === id);
|
||
const el1 = this.elements.find(x => x.id === "titletriangle");
|
||
$('#attr-box').removeClass('d-none');
|
||
$('#inp-text').val(el.text);
|
||
$('#inp-size').val(el.style.fontSize);
|
||
$('#xPosition').val(el.x);
|
||
$("#yPosition").val(el.y);
|
||
$('#2offset').val(el.twoOffset);
|
||
$("#3offset").val(el.threeOffset);
|
||
$("#4offset").val(el.fourOffset);
|
||
$("#breakLen").val(el.breakLen);
|
||
$("#width").val(el.width);
|
||
$("#height").val(el.height);
|
||
$("#textWidth").val(el.textWidth);
|
||
$("#textHeight").val(el.textHeight);
|
||
$("#textSpan").val(el.textSpan);
|
||
|
||
//this.render();
|
||
},
|
||
|
||
updateActive(key, val) {
|
||
console.log("updateActive:", key, val)
|
||
const el = this.elements.find(x => x.id === this.activeId);
|
||
|
||
if (key === 'fontSize') el.style.fontSize = parseFloat(val);
|
||
else if (key === 'width') el.width = parseFloat(val);
|
||
else if (key === 'breakLen') el.breakLen = parseInt(val);
|
||
else if (key === '2offset') el.twoOffset = parseFloat(val);
|
||
else if (key === '3offset') el.threeOffset = parseFloat(val);
|
||
else if (key === '4offset') el.fourOffset = parseFloat(val);
|
||
else if (key === 'height') el.height = parseFloat(val);
|
||
else el[key] = val;
|
||
if (el.type == "roster") {
|
||
const names = el.text.split('\n').filter(s => s.trim());
|
||
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.startX + "mm", top: el.startY + "mm", fontSize: el.style.fontSize + 'pt', fontFamily: el.fontFamily, "z-index": 9999, visibility: el.style.isActive
|
||
}).html(this.renderRoster(names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit), el));
|
||
} else if (el.type == "combined-center") {
|
||
console.log("combined-center");
|
||
|
||
const parts = el.text.split('\n');
|
||
html = `<div class="ancestor-wrapper" style='width:${el.width}px !important;height:${el.height}px !important' >
|
||
<span class="main-name" >${parts[0] || ''}</span>
|
||
<span class="sub-text">${parts[1] || ''}</span>
|
||
</div>`;
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.startX + "mm", top: el.startY + "mm",
|
||
fontSize: el.style.fontSize + 'pt', fontFamily: el.fontFamily,
|
||
"z-index": 9999, visibility: el.style.isActive,
|
||
|
||
}).html(html);
|
||
} else if (this.activeId === "title1") {
|
||
let names = el.text.split('\n').filter(s => s.trim());
|
||
let slice = names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit);
|
||
html = this.renderNameList(slice, el);
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt', fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility
|
||
}).html(html);
|
||
} else if (this.activeId === "alive") {
|
||
let names = el.text.split('\n').filter(s => s.trim());
|
||
let slice = names.slice(0 * this.rosterLimit, (0 + 1) * this.rosterLimit);
|
||
html = this.renderLiveList(slice, el);
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt', fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility
|
||
}).html(html);
|
||
} else if (this.activeId === "righttitle") {
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt',
|
||
fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility,
|
||
width: el.width, height: el.height
|
||
}).text(el.text);
|
||
} else if (this.activeId === "lefttitle") {
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt',
|
||
fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility,
|
||
width: el.width, height: el.height
|
||
}).text(el.text);
|
||
} else if (this.activeId === "address") {
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt',
|
||
fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility,
|
||
width: el.width, height: el.height
|
||
}).text(el.text);
|
||
} else {
|
||
$(`.tablet-element[id="${this.activeId}"]`).css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt', fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility
|
||
}).text(el.text);
|
||
}
|
||
//this.render();
|
||
},
|
||
displayItem(id) {
|
||
console.log("displayItem:", this.elements, id);
|
||
let el = this.elements.find(x => x.id === id);
|
||
el.style.visibility = el.style.visibility === "hidden" ? "" : "hidden";
|
||
//要判斷如果所在位置超出範圍,要拉回來放
|
||
console.log("displayItem:", this.paper, el)
|
||
if (el.x > this.paper.width) {
|
||
el.x = this.paper.width / 2
|
||
}
|
||
if (el.y > this.paper.height) {
|
||
el.y = this.paper.height / 2
|
||
}
|
||
$("#" + id).css({
|
||
position: "absolute", left: el.x + "mm", top: el.y + "mm", fontSize: el.style.fontSize + 'pt', fontFamily: el.style.fontFamily, "z-index": 9999, visibility: el.style.visibility
|
||
})
|
||
},
|
||
|
||
addMulti(prefix, count) {
|
||
for (let i = 1; i <= count; i++) {
|
||
const id = `${prefix}-${Date.now()}-${i}`;
|
||
this.elements.push({
|
||
id, type: 'title', text: `正名${i}`, x: 100 - (i * 30), y: 80,
|
||
style: { fontSize: 24, fontFamily: 'DFKai-sb', isVertical: true }
|
||
});
|
||
}
|
||
//this.render();
|
||
},
|
||
|
||
addCombined() {
|
||
this.stopPropagation();
|
||
|
||
this.elements.push({
|
||
id: 'combined-' + Date.now(), type: 'combined-center', text: '林張\n氏歷代祖先', x: 130, y: 80,
|
||
style: { fontSize: 24, fontFamily: 'DFKai-sb', isVertical: true }
|
||
});
|
||
//this.render();
|
||
},
|
||
addElement() {
|
||
//console.log("QQQ");
|
||
},
|
||
wrapImageInSvg(imageBase64) {
|
||
// 建立一個簡單的 SVG 字串,將圖片鋪滿
|
||
// preserveAspectRatio="none" 確保圖片會拉伸填滿 SVG 容器
|
||
const svgString = `
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" preserveAspectRatio="none">
|
||
<image href="${imageBase64}" x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" />
|
||
</svg>
|
||
`.trim();
|
||
|
||
// 轉成 Data URI 格式
|
||
// 注意:SVG 內容若包含特殊字元需編碼,但 base64 圖片通常是安全的
|
||
// 為了保險,我們使用 encodeURIComponent 對 svgString 編碼
|
||
return `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`;
|
||
},
|
||
clickBackend() {
|
||
$("#backendInp").click();
|
||
},
|
||
bindBackend() {
|
||
$("#backendInp").on('change', function (e) {
|
||
|
||
const input = e.target;
|
||
if (input.files && input.files[0]) {
|
||
const file = input.files[0];
|
||
|
||
// 1. 先讀取檔案轉成 Base64
|
||
const reader = new FileReader();
|
||
reader.onload = (e) => {
|
||
const base64Image = e.target.result; // 這會是 data:image/png;base64,xxxx...
|
||
|
||
// 2. 將 Base64 PNG 包裝成 SVG
|
||
//const svgDataUri = this.wrapImageInSvg(base64Image);
|
||
// 3. 設定到底圖
|
||
//this.service.setBackground(svgDataUri);
|
||
$(".tablet-paper").css({ 'background-image': 'url(' + base64Image + ')', 'background-size': '100% 100%' })
|
||
};
|
||
|
||
reader.readAsDataURL(file);
|
||
}
|
||
})
|
||
},
|
||
async saveStyle() {
|
||
let self = this;
|
||
let detail = [
|
||
|
||
];
|
||
|
||
this.elements.forEach((el) => {
|
||
detail.push({
|
||
elementID: el.id, startX: el.x.toString(), startY: el.y.toString(), fontSize: el.style.fontSize, fontFamily: el.style.fontFamily,
|
||
breakLen: el.breakLen, twoOffset: el.twoOffset, threeOffset: el.threeOffset, fourOffset: el.fourOffset,
|
||
isActive: el.style.visibility, width: el.width, height: el.height, textWidth: el.textWidth, textHeight: el.textHeight, textSpan: el.textSpan
|
||
});
|
||
});
|
||
let sID = this.styleID
|
||
if (sID == "000001") {
|
||
this.styleID = ""
|
||
}
|
||
let master = {
|
||
styleID: this.styleID, styleName: this.styleName, paperSize: this.paperSize,//$("#paperSize").val(),
|
||
backendImg: this.backendInp, printSize: this.printSize, printMode: this.printMode,
|
||
orientation:"", printPageCount:this.perPage, rosterLimit: 8,
|
||
detail: detail
|
||
}
|
||
console.log(master);
|
||
let path = "SavDegignerData";
|
||
if (this.styleID != null && this.styleID != "") {
|
||
path = "UpdateDegignerData"
|
||
}
|
||
await axios
|
||
.post(HTTP_HOST + `api/tablet/${path}`, master)
|
||
.then(response => {
|
||
if (response.status == "200") {
|
||
if (response.data.result == "Y") {
|
||
|
||
$("#toast_body").html(`${"ok!"}`)
|
||
self.toast.show();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
})
|
||
|
||
|
||
//$(() => Designer.init());
|
||
</script>
|
||
</asp:Content>
|
||
|