Files
17168ERP/web/admin/activity/shuwen02.vue
T
2026-08-17 18:05:22 +08:00

558 lines
18 KiB
Vue
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.
<template>
<div id="control-panel">
<div class="control-group">
<label>紙張大小</label><select id="paper-size" onchange="updateLayout()">
<option value="A4" data-w="210" data-h="297" selected>A4</option>
<option value="A3" data-w="297" data-h="420">A3</option>
<option value="B5" data-w="176" data-h="250">B5</option>
</select>
</div>
<div class="control-group">
<label>紙張方向</label><select id="paper-orientation" onchange="updateLayout()">
<option value="portrait" selected>直向</option>
<option value="landscape">橫向</option>
</select>
</div>
<div class="control-group">
<label>一次修改全部行距: <span id="global-margin-lbl">10</span>px</label>
<div class="adjust-row">
<button onclick="adjustAllMargins(-1)">
-
</button><input type="range"
id="global-margin-slide"
min="0"
max="80"
value="10"
oninput="changeAllMargins(this.value)" /><button onclick="adjustAllMargins(1)">+</button>
</div>
</div>
<hr />
<p style="
font-size: 11px;
color: #c0392b;
font-weight: 700;
background: #fde8e7;
padding: 6px;
border-radius: 4px;
margin: 0;
">
💡 提示:在右側文字上【按滑鼠右鍵】觸發設定,面板固定顯示在左下角!
</p>
<hr />
<button class="action-btn"
style="background: #28a745"
onclick="saveData()">
1. 儲存至資料庫
</button>
<button class="action-btn" onclick="window.print()">
2. 產生 PDF / 列印
</button>
</div>
<div id="preview-container" onclick="hideFloatingMenu(event)">
<div id="paper-preview">
<div id="editor"
contenteditable="true"
oncontextmenu="rightClickLineTrack(event)">
<div style="
font-size: 28px;
font-weight: bold;
align-items: center;
margin-left: 25px;
">
中元普渡祈福文疏
</div>
<div></div>
<div style="font-size: 16px; letter-spacing: 4px; margin-left: 12px">
今逢佳節齋供養,花果盡是素佳餚。
</div>
<div style="font-size: 16px; margin-left: 12px">
誠心祈求風調雨順、國泰民安。
</div>
<div></div>
<div style="font-size: 14px; align-items: flex-end; margin-left: 30px">
歲次天運民國一一五[YEAR]年七[MONTH]月十五[DAY]日。
</div>
</div>
</div>
<div id="line-floating-menu" onclick="event.stopPropagation()">
<h4 id="menu-header">✥ 拖拉此處移動面板</h4>
<div class="control-group">
<label>字型大小: <span id="f-size-lbl">16</span>px</label>
<div class="adjust-row">
<button onclick="
stepSingleLine(
'fontSize',
'f-size-slide',
'f-size-lbl',
-1,
'px',
)
">
-
</button><input type="range"
id="f-size-slide"
min="12"
max="60"
value="16"
oninput="
adjustLineStyle(
'fontSize',
this.value + 'px',
'f-size-lbl',
this.value,
)
" /><button onclick="
stepSingleLine(
'fontSize',
'f-size-slide',
'f-size-lbl',
1,
'px',
)
">
+
</button>
</div>
</div>
<div class="control-group">
<label>字距: <span id="f-space-lbl">2</span>px</label>
<div class="adjust-row">
<button onclick="
stepSingleLine(
'letterSpacing',
'f-space-slide',
'f-space-lbl',
-1,
'px',
)
">
-
</button><input type="range"
id="f-space-slide"
min="0"
max="25"
value="2"
oninput="
adjustLineStyle(
'letterSpacing',
this.value + 'px',
'f-space-lbl',
this.value,
)
" /><button onclick="
stepSingleLine(
'letterSpacing',
'f-space-slide',
'f-space-lbl',
1,
'px',
)
">
+
</button>
</div>
</div>
<div class="control-group">
<label>行距(與左側): <span id="f-margin-lbl">10</span>px</label>
<div class="adjust-row">
<button onclick="
stepSingleLine(
'marginLeft',
'f-margin-slide',
'f-margin-lbl',
-1,
'px',
)
">
-
</button><input type="range"
id="f-margin-slide"
min="0"
max="80"
value="10"
oninput="
adjustLineStyle(
'marginLeft',
this.value + 'px',
'f-margin-lbl',
this.value,
)
" /><button onclick="
stepSingleLine(
'marginLeft',
'f-margin-slide',
'f-margin-lbl',
1,
'px',
)
">
+
</button>
</div>
</div>
<div class="control-group">
<label>垂直位置對齊</label>
<div class="menu-btn-group">
<button id="f-align-top"
onclick="adjustLineAlign('flex-start', this)">
上
</button><button id="f-align-center"
onclick="adjustLineAlign('center', this)">
中
</button><button id="f-align-bottom"
onclick="adjustLineAlign('flex-end', this)">
下
</button>
</div>
</div>
</div>
</div>
</template>
<script>
let currentTargetLine = null;
function updateLayout() {
const e = document.getElementById("paper-size"),
t = document.getElementById("paper-orientation"),
n = document.getElementById("paper-preview"),
a = document.getElementById("print-style"),
o = e.options[e.selectedIndex],
i = parseFloat(o.getAttribute("data-w")),
r = parseFloat(o.getAttribute("data-h")),
d = e.value,
l = t.value;
let s = "portrait" === l ? i : r,
c = "portrait" === l ? r : i;
((n.style.width = s + "mm"),
(n.style.height = c + "mm"),
(a.innerHTML = `@media print { @page { size: ${d} ${l}; margin: 0mm; } }`),
hideFloatingMenu());
}
function rightClickLineTrack(e) {
s
let t = e.target;
const n = document.getElementById("editor");
while (t && t.parentNode !== n && t !== n) {
t = t.parentNode;
}
if (!t || t === n || "DIV" !== t.tagName) return;
(e.preventDefault(),
currentTargetLine &&
currentTargetLine.classList.remove("selected-line"),
(currentTargetLine = t),
currentTargetLine.classList.add("selected-line"));
const a = window.getComputedStyle(currentTargetLine),
o = parseInt(a.fontSize) || 16;
((document.getElementById("f-size-slide").value = o),
(document.getElementById("f-size-lbl").innerText = o));
const i = "normal" === a.letterSpacing ? 0 : parseInt(a.letterSpacing);
((document.getElementById("f-space-slide").value = i),
(document.getElementById("f-space-lbl").innerText = i));
const r = parseInt(a.marginLeft) || 10;
((document.getElementById("f-margin-slide").value = r),
(document.getElementById("f-margin-lbl").innerText = r),
document
.querySelectorAll(".menu-btn-group button")
.forEach((e) => e.classList.remove("active")),
"flex-start" === a.alignItems &&
document.getElementById("f-align-top").classList.add("active"),
"center" === a.alignItems &&
document.getElementById("f-align-center").classList.add("active"),
"flex-end" === a.alignItems &&
document.getElementById("f-align-bottom").classList.add("active"),
(document.getElementById("line-floating-menu").style.display =
"flex"));
}
function hideFloatingMenu(e) {
if (
e &&
("preview-container" === e.target.id ||
"paper-preview" === e.target.id)
) {
((document.getElementById("line-floating-menu").style.display =
"none"),
currentTargetLine &&
currentTargetLine.classList.remove("selected-line"),
(currentTargetLine = null));
}
}
function adjustLineStyle(e, t, n, a) {
currentTargetLine &&
((currentTargetLine.style[e] = t),
(document.getElementById(n).innerText = a));
}
function stepSingleLine(e, t, n, a, o) {
if (currentTargetLine) {
const i = document.getElementById(t);
let r = parseInt(i.value) + a;
(r < parseInt(i.min) && (r = parseInt(i.min)),
r > parseInt(i.max) && (r = parseInt(i.max)),
(i.value = r),
adjustLineStyle(e, r + o, n, r));
}
}
function changeAllMargins(e) {
((document.getElementById("global-margin-lbl").innerText = e),
document.querySelectorAll("#editor > div").forEach((t) => {
t.style.marginLeft = e + "px";
}));
}
function adjustAllMargins(e) {
const t = document.getElementById("global-margin-slide");
let n = parseInt(t.value) + e;
(n < parseInt(t.min) && (n = parseInt(t.min)),
n > parseInt(t.max) && (n = parseInt(t.max)),
(t.value = n),
changeAllMargins(n));
}
function adjustLineAlign(e, t) {
currentTargetLine &&
((currentTargetLine.style.alignItems = e),
document
.querySelectorAll(".menu-btn-group button")
.forEach((e) => e.classList.remove("active")),
t.classList.add("active"));
}
function saveData() {
((document.getElementById("line-floating-menu").style.display = "none"),
currentTargetLine &&
currentTargetLine.classList.remove("selected-line"),
console.log(document.getElementById("editor").innerHTML),
alert("已成功擷取 HTML 結構至 Console!"));
}
window.onload = function () {
updateLayout();
};
(function () {
const e = document.getElementById("line-floating-menu"),
t = document.getElementById("menu-header");
let n = !1,
a,
o;
(t.addEventListener("mousedown", (r) => {
const i = e.getBoundingClientRect();
((a = r.clientX - i.left),
(o = r.clientY - i.top),
r.target === t && ((n = !0), (e.style.position = "fixed")));
}),
document.addEventListener("mousemove", (t) => {
if (n) {
t.preventDefault();
let i = t.clientX - a,
r = t.clientY - o;
((e.style.left = i + "px"),
(e.style.top = r + "px"),
(e.style.bottom = "auto"));
}
}),
document.addEventListener("mouseup", () => (n = !1)));
})();
</script>
<style>
@media print {
@page {
size: A4 portrait;
margin: 0mm;
}
}
body {
font-family: "Microsoft JhengHei", sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
display: flex;
gap: 20px;
}
#control-panel {
width: 240px;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 15px;
height: fit-content;
}
.control-group {
display: flex;
flex-direction: column;
gap: 5px;
font-size: 14px;
}
select,
input[type="range"] {
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
}
button.action-btn {
padding: 12px;
background: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
font-weight: 700;
}
#preview-container {
display: flex;
flex-grow: 1;
background: #ddd;
padding: 20px;
overflow: auto;
position: relative;
}
#paper-preview {
background: #fff;
padding: 20mm;
box-sizing: border-box;
width: 210mm;
height: 297mm;
}
#editor {
width: 100%;
height: 100%;
outline: none;
writing-mode: vertical-rl;
text-orient: upright;
}
#editor > div {
min-height: 1em;
display: flex !important;
flex-direction: column;
align-items: flex-start;
margin-left: 10px;
padding: 0 4px;
}
#editor > div:hover {
background: rgba(0, 123, 255, 0.05);
}
#editor > div.selected-line {
background: rgba(0, 123, 255, 0.1) !important;
box-shadow: 0 0 0 1px #ff7675;
}
#line-floating-menu {
display: none;
position: fixed;
bottom: 20px;
left: 280px;
background: #fff0f1;
border: 2px solid #ff7675;
padding: 12px;
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
z-index: 99999;
flex-direction: column;
gap: 8px;
width: 210px;
}
#line-floating-menu h4 {
margin: 0;
color: #d63031;
font-size: 14px;
cursor: move;
background: #ffe4e6;
padding: 4px;
border-radius: 4px;
text-align: center;
user-select: none;
}
.menu-btn-group {
display: flex;
gap: 4px;
}
.menu-btn-group button {
flex: 1;
padding: 6px;
border: 1px solid #ccc;
background: #fff;
cursor: pointer;
border-radius: 4px;
font-weight: 700;
}
.menu-btn-group button.active {
background: #28a745;
color: #fff;
border-color: #28a745;
}
.adjust-row {
display: flex;
align-items: center;
gap: 5px;
}
.adjust-row button {
padding: 4px 10px;
border: 1px solid #ccc;
background: #fff;
cursor: pointer;
border-radius: 4px;
font-weight: 700;
user-select: none;
}
.adjust-row button:hover {
background: #eee;
}
.adjust-row input[type="range"] {
flex-grow: 1;
padding: 0;
}
@media print {
#control-panel,
#line-floating-menu {
display: none !important;
}
body,
#preview-container {
background: #fff;
padding: 0;
margin: 0;
}
#paper-preview {
width: 100% !important;
height: 100% !important;
box-shadow: none;
/* 1. 移除原本的 flex 與置中設定,改為一般的區塊顯示 */
display: block !important;
/* 2. 透過 padding 設定您需要的上邊界與右邊界 (數值可自由調整) */
padding-top: 30mm !important; /* 控制上方留白 */
padding-right: 30mm !important; /* 控制右側留白 */
padding-bottom: 0 !important;
padding-left: 0 !important;
/* padding: 0;
display: flex !important;
justify-content: center !important;
align-items: center !important; */
}
#editor {
height: auto !important;
/* width: auto !important; 加入這行,讓寬度貼合內容 */
max-height: 100% !important;
}
}
</style>