增加信眾牌位範本及疏文
This commit is contained in:
@@ -1363,7 +1363,7 @@
|
||||
<div class="tab-pane fade" id="sec2-page5" role="tabpanel" aria-labelledby="sec2-tab5" v-if="this_id!=''">
|
||||
<v-card class="mx-auto" outlined >
|
||||
<keep-alive>
|
||||
<component :is="currentView">
|
||||
<component :is="currentView" :num="this_id">
|
||||
</component>
|
||||
</keep-alive>
|
||||
</v-card>
|
||||
|
||||
@@ -10,9 +10,18 @@
|
||||
<div id="control-panel">
|
||||
<!-- panel-inner 用於固定內容寬度,避免收合時文字因為寬度變小而折行 -->
|
||||
<div class="panel-inner">
|
||||
<div class="control-group">
|
||||
<label>疏文類型</label>
|
||||
<select v-model="shuwenType" @change="changeShuwenType">
|
||||
<option value="">請選擇</option>
|
||||
<option value="01">消災</option>
|
||||
<option value="02">超薦</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label>疏文樣板</label>
|
||||
<select v-model="defaultTemplate" @change="changeTemplate">
|
||||
<option value="">請選擇</option>
|
||||
<option v-for="item in shuwenList" :key="item.num" :value="item.num">{{ item.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -73,13 +82,14 @@
|
||||
</div>
|
||||
|
||||
<!-- 預覽區塊 -->
|
||||
<div id="preview-container" @click.prevent="hideFloatingMenu">
|
||||
<div id="preview-container" @click="hideFloatingMenu">
|
||||
<div id="paper-preview" :style="paperStyle">
|
||||
<div
|
||||
id="editor"
|
||||
contenteditable="true"
|
||||
ref="editorRef"
|
||||
@contextmenu.prevent="rightClickLineTrack"
|
||||
@contextmenu="rightClickLineTrack"
|
||||
@keydown.enter.prevent="newLine"
|
||||
>
|
||||
<div style="font-size: 28px; font-weight: bold; align-items: center; margin-left: 25px;">中元普渡祈福文疏</div>
|
||||
<div></div>
|
||||
@@ -170,8 +180,11 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: 'Shuwen02',
|
||||
|
||||
data() {
|
||||
props: {
|
||||
num: { type: String }
|
||||
},
|
||||
data() {
|
||||
|
||||
return {
|
||||
isPanelCollapsed: false, // 控制面板收合狀態
|
||||
paperSize: 'A4',
|
||||
@@ -194,7 +207,8 @@ module.exports = {
|
||||
alignItems: 'flex-start'
|
||||
},
|
||||
shuwenList: [],
|
||||
defaultTemplate: {}
|
||||
defaultTemplate: {},
|
||||
shuwenType:""
|
||||
};
|
||||
},
|
||||
|
||||
@@ -211,7 +225,24 @@ module.exports = {
|
||||
this.startY = 0;
|
||||
},
|
||||
|
||||
methods: {
|
||||
methods: {
|
||||
changeShuwenType() {
|
||||
console.log("changeShuwenType:", this.shuwenType)
|
||||
//去資料庫抓回他的消災或超薦的總疏文
|
||||
axios.post(HTTP_HOST + 'api/shuwens/GetActShuwen', { act_num: Number(this.num), shuwen_type: this.shuwenType })
|
||||
.then(response => {
|
||||
console.log(response)
|
||||
if (response && response.data && response.data.data) {
|
||||
let shuwenContent = response.data.data.contents
|
||||
document.getElementById("editor").innerHTML = `${shuwenContent}`;
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => console.log(error))
|
||||
},
|
||||
newLine() {
|
||||
document.execCommand('insertParagraph',false);
|
||||
},
|
||||
updateLayout() {
|
||||
const sizes = {
|
||||
'A4': { w: 210, h: 297 },
|
||||
@@ -232,7 +263,11 @@ module.exports = {
|
||||
this.initData();
|
||||
},
|
||||
changeTemplate() {
|
||||
// console.log("changeTemplate:", this.defaultTemplate)
|
||||
// console.log("changeTemplate:", this.defaultTemplate)
|
||||
if (this.defaultTemplate === "") {
|
||||
this.clearContent();
|
||||
return
|
||||
}
|
||||
let temp = this.shuwenList.find((x) => x.num === this.defaultTemplate);
|
||||
if (temp) {
|
||||
document.getElementById("editor").innerHTML = temp.contents;
|
||||
@@ -322,7 +357,8 @@ module.exports = {
|
||||
this.isMenuVisible = false;
|
||||
if (this.currentTargetLine) {
|
||||
this.currentTargetLine.classList.remove('selected-line');
|
||||
}
|
||||
}
|
||||
console.log(this.num, this.shuwenType, this.defaultTemplate);
|
||||
const editor = this.$refs.editorRef;
|
||||
if (editor) {
|
||||
console.log(editor.innerHTML);
|
||||
@@ -367,9 +403,7 @@ module.exports = {
|
||||
document.removeEventListener('mouseup', this.stopDrag);
|
||||
},
|
||||
initData() {
|
||||
axios
|
||||
|
||||
.post(HTTP_HOST + 'api/shuwens/ListShuwenTemplate')
|
||||
axios.post(HTTP_HOST + 'api/shuwens/ListShuwenTemplate')
|
||||
.then(response => {
|
||||
console.log(response)
|
||||
this.shuwenList = response.data.data
|
||||
@@ -379,7 +413,7 @@ module.exports = {
|
||||
},
|
||||
clearContent() {
|
||||
document.getElementById("editor").innerHTML = `
|
||||
<div style="font-size: 28px; font-weight: bold; align-items: flex-start; margin-left: 5px; letter-spacing: 16px;" class="">
|
||||
<div class="selected-line">
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
@@ -607,7 +641,8 @@ module.exports = {
|
||||
|
||||
mounted() {
|
||||
this.updateLayout();
|
||||
this.bindEvents();
|
||||
this.bindEvents();
|
||||
document.execCommand("defaultParagraphSeparator",false,'div');
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
@@ -625,7 +660,7 @@ module.exports = {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
/* 基礎排版與重設 */
|
||||
.print-preview-app {
|
||||
font-family: "Microsoft JhengHei", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
|
||||
+36
-20
@@ -2,6 +2,7 @@
|
||||
|
||||
<%@ Register Src="~/admin/_uc/alert.ascx" TagPrefix="uc1" TagName="alert" %>
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" runat="Server">
|
||||
<script src="/js/httpVueLoader.js"></script>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="footer_script" runat="Server">
|
||||
<script>
|
||||
@@ -72,11 +73,11 @@
|
||||
$('#L1').text(data.cDate)
|
||||
$('#L2').text(data.sign)
|
||||
} else {
|
||||
console.log(data.msg);
|
||||
//console.log(data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log(_date);
|
||||
// console.log(_date);
|
||||
}
|
||||
function checkNumber(btn) {
|
||||
$('#errormsg').hide();
|
||||
@@ -98,7 +99,7 @@
|
||||
if (data.isOk) {
|
||||
|
||||
} else {
|
||||
console.log(data.msg);
|
||||
//console.log(data.msg);
|
||||
$('#errormsg').text(data.msg);
|
||||
$('#errormsg').show();
|
||||
}
|
||||
@@ -159,7 +160,7 @@
|
||||
],
|
||||
selected: {},
|
||||
select(item, index, t) {
|
||||
console.log("select search1", t);
|
||||
//console.log("select search1", t);
|
||||
}
|
||||
},
|
||||
search2: {
|
||||
@@ -215,7 +216,7 @@
|
||||
],
|
||||
selected: {},
|
||||
select(item, index, t) {
|
||||
console.log("select search3", t);
|
||||
//console.log("select search3", t);
|
||||
}
|
||||
},
|
||||
search4: {
|
||||
@@ -231,7 +232,7 @@
|
||||
],
|
||||
selected: {},
|
||||
select(item, index, t) {
|
||||
console.log("select search4", t);
|
||||
//console.log("select search4", t);
|
||||
//edit item
|
||||
item.appellation_id_selected.text = t.title //=====?
|
||||
item.appellation_id_selected.val = t.num //=====?
|
||||
@@ -255,7 +256,7 @@
|
||||
],
|
||||
selected: {},
|
||||
select(item, index, t) {
|
||||
console.log("select search5", t);
|
||||
//console.log("select search5", t);
|
||||
|
||||
}
|
||||
},
|
||||
@@ -547,7 +548,8 @@
|
||||
{ text: '酉時 (17:00~19:00)', value: '酉時' },
|
||||
{ text: '戌時 (19:00~21:00)', value: '戌時' },
|
||||
{ text: '亥時 (21:00~23:00)', value: '亥時' }
|
||||
]
|
||||
],
|
||||
currentView: 'shuwen-component',
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -566,7 +568,7 @@
|
||||
handler() {
|
||||
//console.log("watch1", this.search_dialog, this.search_dialog.page);
|
||||
this.search_get()
|
||||
console.log("watch2", this.search_dialog, this.search_dialog.page);
|
||||
//console.log("watch2", this.search_dialog, this.search_dialog.page);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
@@ -602,6 +604,9 @@
|
||||
color: 'error'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'shuwen-component': httpVueLoader('./tablet_template.vue'),
|
||||
},
|
||||
methods: {
|
||||
initAutoEnroll() {
|
||||
if (this.follower_id != '') {
|
||||
@@ -743,7 +748,7 @@
|
||||
this.confirm_dialog.pendingDeleteItem = null; // 清空暫存
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("刪除失敗", error);
|
||||
//console.log("刪除失敗", error);
|
||||
this.snackbar.text = "刪除失敗:" + error;
|
||||
this.snackbar.show = true;
|
||||
})
|
||||
@@ -915,13 +920,13 @@
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("刪除訂單失敗", error);
|
||||
//console.log("刪除訂單失敗", error);
|
||||
this.snackbar.text = "刪除訂單失敗:" + error;
|
||||
this.snackbar.show = true;
|
||||
})
|
||||
},
|
||||
search_show(curr) {
|
||||
console.log("btn_click:", curr, curr.api_url);
|
||||
//console.log("btn_click:", curr, curr.api_url);
|
||||
this.search_dialog.current = curr;
|
||||
this.search_clear()
|
||||
//this.search_get()//清除完自動會重抓, 故取消
|
||||
@@ -961,14 +966,14 @@
|
||||
}
|
||||
// }
|
||||
|
||||
console.log("search_get", api_url, search, params, this.options);
|
||||
//console.log("search_get", api_url, search, params, this.options);
|
||||
this.search_dialog.loading = true
|
||||
axios.post(api_url, search, { params: params })
|
||||
.then(response => {
|
||||
this.search_dialog.list = response.data.list
|
||||
this.search_dialog.count = response.data.count
|
||||
this.search_dialog.loading = false
|
||||
console.log(this.search_dialog.list)
|
||||
//console.log(this.search_dialog.list)
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
@@ -1079,7 +1084,7 @@
|
||||
axios
|
||||
.post(HTTP_HOST + 'api/follower/familyDelete', pro_order_detail)
|
||||
.then(response => {
|
||||
console.log("del", item);
|
||||
// console.log("del", item);
|
||||
this.desserts.splice(index, 1)
|
||||
this.desserts_count = this.desserts_count - 1;
|
||||
})
|
||||
@@ -1354,7 +1359,7 @@
|
||||
axios
|
||||
.delete(HTTP_HOST + 'api/follower/tabletDelete/' + item.num)
|
||||
.then(response => {
|
||||
console.log("del", item);
|
||||
//console.log("del", item);
|
||||
this.tabletTable.desserts.splice(index, 1)
|
||||
this.tabletTable.desserts_count = this.tabletTable.desserts_count - 1;
|
||||
})
|
||||
@@ -1388,7 +1393,7 @@
|
||||
})
|
||||
},
|
||||
getFamilyMembers() {
|
||||
console.log("getFamilyMembers", this.follower_id);
|
||||
// console.log("getFamilyMembers", this.follower_id);
|
||||
if (this.follower_id) {
|
||||
this.family.loading = true;
|
||||
// 假設您有一個 API 端點來獲取特定 follower 的 family members
|
||||
@@ -1400,7 +1405,7 @@
|
||||
birthdate: member.birthdate ? member.birthdate.split('T')[0] : null
|
||||
}));
|
||||
this.family.members_count = response.data.length;
|
||||
console.log("family.members", this.family.members);
|
||||
//console.log("family.members", this.family.members);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching family members:", error);
|
||||
@@ -1435,7 +1440,7 @@
|
||||
//this.editedItem = $.extend(true, {}, this.defaultItem);
|
||||
},
|
||||
fam_save(item) {
|
||||
console.log("fam_save 1:", this.family.edItem);
|
||||
//console.log("fam_save 1:", this.family.edItem);
|
||||
|
||||
// 驗證必填欄位 - 只有姓名必填
|
||||
if (!this.family.edItem.fam_name || this.family.edItem.fam_name.trim() === '') {
|
||||
@@ -1458,7 +1463,7 @@
|
||||
|
||||
savePromise
|
||||
.then(response => {
|
||||
console.log("fam_save 2: Server response", response);
|
||||
//console.log("fam_save 2: Server response", response);
|
||||
|
||||
//const updatedMember = fam_item;
|
||||
const updatedMember = { ...fam_item, num: response.data.num };
|
||||
@@ -1766,6 +1771,9 @@
|
||||
<button class="nav-link" id="sec2-tab5" data-bs-toggle="tab" data-bs-target="#sec2-page5"
|
||||
type="button" role="tab" aria-controls="profile" aria-selected="false">
|
||||
全年報名 </button>
|
||||
<button class="nav-link" id="sec2-tab6" data-bs-toggle="tab" data-bs-target="#sec2-page6"
|
||||
type="button" role="tab" aria-controls="profile" aria-selected="false">
|
||||
牌位範本 </button>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -2488,6 +2496,14 @@
|
||||
</v-card>
|
||||
|
||||
</div>
|
||||
<div class="tab-pane fade" id="sec2-page6" role="tabpanel" aria-labelledby="sec2-tab6" >
|
||||
<v-card class="mx-auto" outlined v-if="follower_id!=''">
|
||||
<keep-alive>
|
||||
<component :is="currentView" >
|
||||
</component>
|
||||
</keep-alive>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</asp:Panel>
|
||||
|
||||
@@ -1894,7 +1894,7 @@
|
||||
const data = typeof f_num_tablet === 'string'
|
||||
? JSON.parse(f_num_tablet)
|
||||
: f_num_tablet;
|
||||
|
||||
console.log("data:",data);
|
||||
// 格式化函數
|
||||
const formatItems = (items) => {
|
||||
if (!items || !Array.isArray(items)) return '';
|
||||
@@ -1907,7 +1907,9 @@
|
||||
|
||||
return {
|
||||
mid: formatItems(data.mid_items),
|
||||
left: formatItems(data.left_items)
|
||||
left: formatItems(data.left_items),
|
||||
mid_left: formatItems(data.mid_left),
|
||||
mid_right: formatItems(data.mid_right)
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('Error in getTabletText:', e);
|
||||
@@ -2172,6 +2174,14 @@
|
||||
<span class="badge rounded-pill bg-secondary">陽上</span>
|
||||
<span v-html="getTabletText(item.f_num_tablet).left"></span>
|
||||
</div>
|
||||
<div v-if="getTabletText(item.f_num_tablet).mid_left">
|
||||
<span class="badge rounded-pill bg-secondary">左正名</span>
|
||||
<span v-html="getTabletText(item.f_num_tablet).mid_left"></span>
|
||||
</div>
|
||||
<div v-if="getTabletText(item.f_num_tablet).mid_right">
|
||||
<span class="badge rounded-pill bg-secondary">右正名</span>
|
||||
<span v-html="getTabletText(item.f_num_tablet).mid_right"></span>
|
||||
</div>
|
||||
<div class="d-none">
|
||||
{{item.f_num_tablet}}
|
||||
</div>
|
||||
|
||||
@@ -102,14 +102,14 @@
|
||||
<v-icon left>mdi-currency-usd</v-icon>
|
||||
收入統計
|
||||
</v-tab>
|
||||
<v-tab :disabled="!hasData">
|
||||
<%-- <v-tab :disabled="!hasData">
|
||||
<v-icon left>mdi-chart-line</v-icon>
|
||||
趨勢分析
|
||||
</v-tab>
|
||||
<v-tab :disabled="!hasData">
|
||||
<v-icon left>mdi-compare</v-icon>
|
||||
對比分析
|
||||
</v-tab>
|
||||
</v-tab>--%>
|
||||
</v-tabs>
|
||||
|
||||
<!-- 頁籤內容 -->
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
const designer = {
|
||||
zoomScale:1,
|
||||
zoomScale:0.5,
|
||||
code: {
|
||||
style: []
|
||||
},
|
||||
@@ -1498,223 +1498,158 @@
|
||||
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 h = parseFloat(d.height);
|
||||
let w = parseFloat(d.width);
|
||||
let th = parseFloat(d.textHeight) || h;
|
||||
let v_gap = parseFloat(d.textSpan) || 0;
|
||||
let h_gap = 5;
|
||||
|
||||
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;
|
||||
let shrinkLimit = 6;
|
||||
if (h < 500) {
|
||||
shrinkLimit = 8;
|
||||
} else if (h < 300) {
|
||||
shrinkLimit = 12;
|
||||
}
|
||||
|
||||
// 處理極端情況
|
||||
if (sizes.length === 0) {
|
||||
sizes = names.map(name => {
|
||||
return { name: name, size: 12, isMulti: (name.length * 12 * 1.333) > th };
|
||||
});
|
||||
let long_s = parseFloat(d.fontSize);
|
||||
let short_s = parseFloat(d.fontSize);
|
||||
|
||||
if (names.length == 1 && names[0].includes("暨無始劫以來累世冤親債主")) {
|
||||
names = names[0].split("暨");
|
||||
names[0] = names[0] + "暨";
|
||||
}
|
||||
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
|
||||
|
||||
// 1. 固定的長短名判斷
|
||||
let items = names.map(name => {
|
||||
let l = name.length;
|
||||
let isShort = (names.length > 1) && ((l * 40) <= (th + (v_gap / 2)));
|
||||
return { name: name, l: l, isShort: isShort, isMulti: false, size: 0 };
|
||||
});
|
||||
|
||||
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 max_short_effective = shrinkLimit;
|
||||
//let min_long_limit = parseFloat(d.fontSize);
|
||||
|
||||
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",
|
||||
//items.forEach(item => {
|
||||
// if (item.isShort) {
|
||||
// let max_s_for_th = Math.floor(th / (item.l * 1.333));
|
||||
// if (max_s_for_th > max_short_effective) max_short_effective = max_s_for_th;
|
||||
// } else {
|
||||
// let max_s_for_h = Math.floor((h - 10) / (item.l * 1.333));
|
||||
// if (max_s_for_h < min_long_limit) min_long_limit = max_s_for_h;
|
||||
// }
|
||||
//});
|
||||
|
||||
//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);
|
||||
//if (short_s > max_short_effective) short_s = max_short_effective;
|
||||
//if (long_s > min_long_limit) long_s = min_long_limit;
|
||||
|
||||
// // 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) {
|
||||
let h = Number(el.height);
|
||||
let w = Number(el.width);
|
||||
let th = Number(el.textHeight) || h;
|
||||
let sizes = [];
|
||||
let shrink_turn = 'long';
|
||||
|
||||
// 1. 單純計算總共有幾行 (列)
|
||||
let namesPerCol = Math.floor(h / th);
|
||||
if (namesPerCol < 1) namesPerCol = 1;
|
||||
while (true) {
|
||||
let totalWidth = 0;
|
||||
let current_col_h = 0;
|
||||
let current_col_w = 0;
|
||||
let cols_width = [];
|
||||
let any_wrapped = false;
|
||||
|
||||
let cols = Math.ceil(names.length / namesPerCol); // 總行數
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
let s = item.isShort ? short_s : long_s;
|
||||
|
||||
// 2. 根據行數,縮小字體直到總寬度放得下
|
||||
let s = Number(el.fontSize);
|
||||
while (s >= 4) {
|
||||
let s_px = s * (4 / 3); // pt 轉 px
|
||||
let lh = 1.1; // 行高
|
||||
let gap = 0; // 對應 span 的 margin-left
|
||||
if (item.isShort) {
|
||||
let max_s_for_th = Math.floor(th / (item.l * 1.333));
|
||||
if (s > max_s_for_th) s = max_s_for_th;
|
||||
}
|
||||
|
||||
// 總寬度 = 總行數 * (單行像素寬度 + 間距)
|
||||
let totalWidth = cols * (s_px * lh + gap);
|
||||
item.size = s;
|
||||
let size_px = s * 1.333;
|
||||
|
||||
// 放得下就停止縮小
|
||||
if (totalWidth <= w) {
|
||||
if (item.isShort) {
|
||||
let item_h = Math.max(th, item.l * size_px);
|
||||
if (current_col_h === 0) {
|
||||
current_col_h = item_h;
|
||||
current_col_w = size_px;
|
||||
} else if ((current_col_h + v_gap + item_h) <= h) {
|
||||
current_col_h += (v_gap + item_h);
|
||||
if (size_px > current_col_w) current_col_w = size_px;
|
||||
} else {
|
||||
cols_width.push(current_col_w);
|
||||
current_col_h = item_h;
|
||||
current_col_w = size_px;
|
||||
}
|
||||
} else {
|
||||
if (current_col_h > 0) {
|
||||
cols_width.push(current_col_w);
|
||||
current_col_h = 0;
|
||||
current_col_w = 0;
|
||||
}
|
||||
|
||||
let item_w = size_px;
|
||||
if ((item.l * size_px) > (h - 10)) {
|
||||
item.isMulti = true;
|
||||
any_wrapped = true;
|
||||
let chars_per_col = Math.floor((h - 10) / size_px);
|
||||
if (chars_per_col < 1) chars_per_col = 1;
|
||||
let item_cols = Math.ceil(item.l / chars_per_col);
|
||||
item_w = item_cols * (size_px * 1.1);
|
||||
} else {
|
||||
item.isMulti = false;
|
||||
}
|
||||
|
||||
cols_width.push(item_w);
|
||||
}
|
||||
}
|
||||
|
||||
if (current_col_h > 0) cols_width.push(current_col_w);
|
||||
|
||||
totalWidth = cols_width.reduce((a, b) => a + b, 0) + (cols_width.length > 1 ? (cols_width.length - 1) * h_gap : 0);
|
||||
|
||||
if (totalWidth <= w && (!any_wrapped || long_s === shrinkLimit)) {
|
||||
sizes = items.map(x => {
|
||||
let final_s = x.size;
|
||||
if (x.isShort) {
|
||||
let max_s_for_th = Math.floor(th / (x.l * 1.333));
|
||||
if (final_s > max_s_for_th) final_s = max_s_for_th;
|
||||
}
|
||||
return { name: x.name, size: final_s, isMulti: x.isMulti, isShort: x.isShort };
|
||||
});
|
||||
break;
|
||||
}
|
||||
s -= 1;
|
||||
|
||||
if (any_wrapped && long_s > shrinkLimit) {
|
||||
long_s--;
|
||||
} else {
|
||||
let hasLong = items.some(x => !x.isShort);
|
||||
let hasShort = items.some(x => x.isShort);
|
||||
|
||||
if (hasLong && hasShort) {
|
||||
if (shrink_turn === 'long') {
|
||||
if (long_s > shrinkLimit) { long_s--; shrink_turn = 'short'; }
|
||||
else if (short_s > shrinkLimit) { short_s--; shrink_turn = 'long'; }
|
||||
else break;
|
||||
} else {
|
||||
if (short_s > shrinkLimit) { short_s--; shrink_turn = 'long'; }
|
||||
else if (long_s > shrinkLimit) { long_s--; shrink_turn = 'short'; }
|
||||
else break;
|
||||
}
|
||||
} else if (hasLong) {
|
||||
if (long_s > shrinkLimit) long_s--;
|
||||
else break;
|
||||
} else {
|
||||
if (short_s > shrinkLimit) short_s--;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s < 4) s = 4;
|
||||
if (sizes.length === 0) {
|
||||
sizes = names.map(name => {
|
||||
return { name: name, size: shrinkLimit, isMulti: false, isShort: true };
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 建立外層 Flexbox 容器
|
||||
let $namelist = $(`<div class='liveList'></div>`).css({
|
||||
let $namelist = $(`<div class='nameList'></div>`).css({
|
||||
"writing-mode": "vertical-rl",
|
||||
display: "flex",
|
||||
"flex-direction": "row",
|
||||
@@ -1724,66 +1659,139 @@
|
||||
margin: "auto",
|
||||
width: `${w}px`,
|
||||
height: `${h}px`,
|
||||
border: "0px solid #ccc",
|
||||
padding: "0px",
|
||||
border: "0px solid transparent",
|
||||
padding: "1px",
|
||||
"column-gap": `${v_gap}px`,
|
||||
"font-family": d.fontFamily
|
||||
});
|
||||
|
||||
sizes.forEach(item => {
|
||||
let minH = item.isShort ? `${th}px` : `${h}px`;
|
||||
let alignStyle = item.isMulti || (!item.isMulti && !item.isShort) ? "start" : "justify";
|
||||
|
||||
let $span = $(`<span style="font-size: ${item.size}pt;">${item.name}</span>`).css({
|
||||
display: "block",
|
||||
"min-height": minH,
|
||||
"max-height": `${h}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) {
|
||||
let h = parseFloat(el.height);
|
||||
let w = parseFloat(el.width);
|
||||
let th = parseFloat(el.textHeight) || h;
|
||||
|
||||
let v_gap = parseFloat(el.textSpan) || 0;
|
||||
let h_gap = 5;
|
||||
|
||||
let shrinkLimit = 4;
|
||||
if (h < 300) {
|
||||
shrinkLimit = 8;
|
||||
} else if (h < 500) {
|
||||
shrinkLimit = 6;
|
||||
}
|
||||
|
||||
let s = parseFloat(el.fontSize);
|
||||
|
||||
while (s >= shrinkLimit) {
|
||||
let size_px = s * 1.333;
|
||||
let current_col_h = 0;
|
||||
let current_col_w = 0;
|
||||
let cols_width = [];
|
||||
let any_wrapped = false;
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
let l = names[i].length;
|
||||
let item_h = Math.max(th, l * size_px);
|
||||
|
||||
if (item_h > (h - 5)) {
|
||||
any_wrapped = true;
|
||||
if (current_col_h > 0) {
|
||||
cols_width.push(current_col_w);
|
||||
current_col_h = 0;
|
||||
current_col_w = 0;
|
||||
}
|
||||
let chars_per_col = Math.floor((h - 5) / size_px);
|
||||
if (chars_per_col < 1) chars_per_col = 1;
|
||||
let item_cols = Math.ceil(l / chars_per_col);
|
||||
cols_width.push(item_cols * (size_px * 1.1));
|
||||
} else {
|
||||
if (current_col_h === 0) {
|
||||
current_col_h = item_h;
|
||||
current_col_w = size_px;
|
||||
} else if (current_col_h + v_gap + item_h <= h) {
|
||||
current_col_h += v_gap + item_h;
|
||||
if (size_px > current_col_w) current_col_w = size_px;
|
||||
} else {
|
||||
cols_width.push(current_col_w);
|
||||
current_col_h = item_h;
|
||||
current_col_w = size_px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_col_h > 0) cols_width.push(current_col_w);
|
||||
|
||||
let totalWidth = cols_width.reduce((a, b) => a + b, 0) + (cols_width.length > 1 ? (cols_width.length - 1) * h_gap : 0);
|
||||
|
||||
if (totalWidth <= w && (!any_wrapped || s === shrinkLimit)) {
|
||||
break;
|
||||
}
|
||||
s -= 1;
|
||||
}
|
||||
|
||||
if (s < shrinkLimit) s = shrinkLimit;
|
||||
|
||||
let $namelist = $(`<div class='liveList'></div>`).css({
|
||||
"writing-mode": "vertical-rl",
|
||||
"display": "flex",
|
||||
"flex-flow": "wrap",
|
||||
"align-content": "center",
|
||||
"align-items": "center",
|
||||
"margin": "auto",
|
||||
"width": `${w}px`,
|
||||
"height": `${h}px`,
|
||||
"border": "0px solid transparent",
|
||||
"padding": "0px",
|
||||
"font-family": el.fontFamily,
|
||||
"column-gap": `${el.textSpan}px`,
|
||||
"column-gap": `${v_gap}px`
|
||||
});
|
||||
|
||||
let self = this;
|
||||
names.forEach(name => {
|
||||
$namelist.append(self.renderLiveSpan(name, el, s));
|
||||
});
|
||||
//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, fontSize) {
|
||||
let th = Number(el.textHeight) || Number(el.height);
|
||||
let h = parseFloat(el.height);
|
||||
let th = parseFloat(el.textHeight) || h;
|
||||
|
||||
return $(`<span style="font-size: ${fontSize}pt;">${name}</span>`).css({
|
||||
display: "block",
|
||||
"min-height": `${th}px`, // ★ 固定分配高度,交給 Flexbox 自動排版
|
||||
"max-height": `${el.height}px`,
|
||||
width: `${fontSize * 1.33}`,
|
||||
//"line-height": "1.1",
|
||||
"text-align": "justify", // 統一分散對齊
|
||||
"display": "block",
|
||||
"min-height": `${th}px`,
|
||||
"max-height": `${h}px`,
|
||||
"width": "auto",
|
||||
"text-align": "justify",
|
||||
"text-align-last": "justify",
|
||||
//"margin-left": "5px",
|
||||
"margin-left": "0px",
|
||||
"text-justify": "inter-character",
|
||||
"word-break": "break-all",
|
||||
"white-space": "pre-line"
|
||||
});
|
||||
//return $(`<span class="liveSpan">${name}</span>`).css({
|
||||
// display: "block",
|
||||
// "min-height": `${el.textHeight}px`,
|
||||
// "max-height": `${el.height}px`,
|
||||
// width: `${colWidth}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) {
|
||||
|
||||
Reference in New Issue
Block a user