新增: 牌位自訂名稱

This commit is contained in:
2025-11-29 10:56:19 +08:00
parent 27f916eb9c
commit 169a771566
2 changed files with 205 additions and 39 deletions

View File

@@ -88,6 +88,14 @@
padding: 0; padding: 0;
margin-right: 4px; margin-right: 4px;
} }
/* num=0 刪除按鈕警示樣式 */
.btn-delete-warning:hover {
background-color: #fff3cd !important;
border-radius: 50%;
}
.btn-delete-warning:hover .v-icon {
color: #856404 !important;
}
</style> </style>
<link rel="stylesheet" href="print.css"> <link rel="stylesheet" href="print.css">
</head> </head>
@@ -127,16 +135,30 @@
</v-col> </v-col>
<v-col cols="4" md="4" id="type_edit_B" v-if="item_type==='B'" class="d-flex flex-column"> <v-col cols="4" md="4" id="type_edit_B" v-if="item_type==='B'" class="d-flex flex-column">
<v-card elevation="2" class="mb-2 equal-card"> <v-card elevation="2" class="mb-2 equal-card">
<v-checkbox v-model="isAllSelected" <v-card-text class="py-2 px-2">
@change="toggleSelectAll" <v-row no-gutters align="center">
color="primary" <v-col cols="auto">
hide-details <v-checkbox v-model="isAllSelected"
class="checkbox-narrow d-inline-flex"></v-checkbox> @change="toggleSelectAll"
<v-card-subtitle class="d-inline-flex ">疏文代表</v-card-subtitle> color="primary"
<v-card-subtitle class="d-inline-flex ml-2">超渡-已選</v-card-subtitle> hide-details
label="疏文代表"
class="ma-0"></v-checkbox>
</v-col>
<v-col cols="auto" class="ml-4">
<span class="subtitle-2 grey--text text--darken-1">超渡-已選</span>
</v-col>
<v-spacer></v-spacer>
<v-col cols="auto">
<v-btn color="primary" icon small @click="openAddDialog('Y')">
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-col>
</v-row>
</v-card-text>
<v-list class="scrollable-list"> <v-list class="scrollable-list">
<v-list-item-group> <v-list-item-group>
<v-list-item v-for="(member, index) in family_deceased_Y_selected" :key="member.num" class="hover-item"> <v-list-item v-for="(member, index) in family_deceased_Y_selected" :key="index" class="hover-item">
<v-list-item-action> <v-list-item-action>
<v-checkbox v-model="member.IsShuWen" <v-checkbox v-model="member.IsShuWen"
color="primary" color="primary"
@@ -152,13 +174,16 @@
</v-chip> </v-chip>
</v-list-item-title> </v-list-item-title>
<v-list-item-subtitle class="d-flex justify-end"> <v-list-item-subtitle class="d-flex justify-end">
<v-btn v-if="member.num === 0" icon @click="toggleNoSpace(member)" :title="'不加空格'">
<v-icon :color="member.nospace ? 'green' : 'grey lighten-1'">mdi-backspace</v-icon>
</v-btn>
<v-btn icon @click="moveUp(member, 'Y', index)" :disabled="index === 0"> <v-btn icon @click="moveUp(member, 'Y', index)" :disabled="index === 0">
<v-icon color="grey lighten-1">mdi-arrow-up</v-icon> <v-icon color="grey lighten-1">mdi-arrow-up</v-icon>
</v-btn> </v-btn>
<v-btn icon @click="moveDown(member, 'Y', index)" :disabled="index === family_deceased_Y_selected.length - 1"> <v-btn icon @click="moveDown(member, 'Y', index)" :disabled="index === family_deceased_Y_selected.length - 1">
<v-icon color="grey lighten-1">mdi-arrow-down</v-icon> <v-icon color="grey lighten-1">mdi-arrow-down</v-icon>
</v-btn> </v-btn>
<v-btn icon @click="removeFromSelected(member, 'Y')"> <v-btn icon @click="confirmRemove(member, index, 'Y')" :class="{'btn-delete-warning': member.num === 0}">
<v-icon color="grey lighten-1">mdi-minus</v-icon> <v-icon color="grey lighten-1">mdi-minus</v-icon>
</v-btn> </v-btn>
</v-list-item-subtitle> </v-list-item-subtitle>
@@ -200,16 +225,30 @@
</v-col> </v-col>
<v-col cols="4" md="4" id="type_edit_AB" class="d-flex flex-column"> <v-col cols="4" md="4" id="type_edit_AB" class="d-flex flex-column">
<v-card elevation="2" class="mb-2 equal-card"> <v-card elevation="2" class="mb-2 equal-card">
<v-checkbox v-model="isAllSelected" <v-card-text class="py-2 px-2">
@change="toggleSelectAll" <v-row no-gutters align="center">
color="primary" <v-col cols="auto" v-if="item_type==='A'">
hide-details <v-checkbox v-model="isAllSelected"
class="checkbox-narrow d-inline-flex align-center" v-if="item_type==='A'"></v-checkbox> @change="toggleSelectAll"
<v-card-subtitle class="d-inline-flex align-center" v-if="item_type==='A'">疏文代表</v-card-subtitle> color="primary"
<v-card-subtitle class="d-inline-flex align-center ml-2">陽上/祈福-已選</v-card-subtitle> hide-details
label="疏文代表"
class="ma-0"></v-checkbox>
</v-col>
<v-col cols="auto" :class="{'ml-4': item_type==='A'}">
<span class="subtitle-2 grey--text text--darken-1">陽上/祈福-已選</span>
</v-col>
<v-spacer></v-spacer>
<v-col cols="auto">
<v-btn color="primary" icon small @click="openAddDialog('N')">
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-col>
</v-row>
</v-card-text>
<v-list class="scrollable-list"> <v-list class="scrollable-list">
<v-list-item-group> <v-list-item-group>
<v-list-item v-for="(member, index) in family_deceased_N_selected" :key="member.num" class="hover-item"> <v-list-item v-for="(member, index) in family_deceased_N_selected" :key="index" class="hover-item">
<v-list-item-action v-if="item_type==='A'"> <v-list-item-action v-if="item_type==='A'">
<v-checkbox v-model="member.IsShuWen" <v-checkbox v-model="member.IsShuWen"
color="primary" color="primary"
@@ -225,13 +264,16 @@
</v-chip> </v-chip>
</v-list-item-title> </v-list-item-title>
<v-list-item-subtitle class="d-flex justify-end"> <v-list-item-subtitle class="d-flex justify-end">
<v-btn v-if="member.num === 0" icon @click="toggleNoSpace(member)" :title="'不加空格'">
<v-icon :color="member.nospace ? 'green' : 'grey lighten-1'">mdi-backspace</v-icon>
</v-btn>
<v-btn icon @click="moveUp(member, 'N', index)" :disabled="index === 0"> <v-btn icon @click="moveUp(member, 'N', index)" :disabled="index === 0">
<v-icon color="grey lighten-1">mdi-arrow-up</v-icon> <v-icon color="grey lighten-1">mdi-arrow-up</v-icon>
</v-btn> </v-btn>
<v-btn icon @click="moveDown(member, 'N', index)" :disabled="index === family_deceased_N_selected.length - 1"> <v-btn icon @click="moveDown(member, 'N', index)" :disabled="index === family_deceased_N_selected.length - 1">
<v-icon color="grey lighten-1">mdi-arrow-down</v-icon> <v-icon color="grey lighten-1">mdi-arrow-down</v-icon>
</v-btn> </v-btn>
<v-btn icon @click="removeFromSelected(member, 'N')"> <v-btn icon @click="confirmRemove(member, index, 'N')" :class="{'btn-delete-warning': member.num === 0}">
<v-icon color="grey lighten-1">mdi-minus</v-icon> <v-icon color="grey lighten-1">mdi-minus</v-icon>
</v-btn> </v-btn>
</v-list-item-subtitle> </v-list-item-subtitle>
@@ -276,6 +318,66 @@
<div style="display:block;"> <div style="display:block;">
<textarea id="desc_iframe" style="width: 900px; height: 100px;">{{tabletItem}}</textarea> <textarea id="desc_iframe" style="width: 900px; height: 100px;">{{tabletItem}}</textarea>
</div> </div>
<!-- 新增項目對話框 -->
<v-dialog v-model="addDialog" max-width="400">
<v-card>
<v-card-title style="font-size: 1rem;">新增項目</v-card-title>
<v-card-text>
<div class="mb-2">
<v-chip
v-for="(phrase, idx) in phrases"
:key="idx"
small
class="mr-1 mb-1"
:title="phrase.length > 5 ? phrase : ''"
@click="newItemText = (newItemText || '') + phrase"
style="cursor: pointer;"
>{{ phrase.length > 5 ? phrase.slice(0, 5) + '...' : phrase }}</v-chip>
</div>
<v-text-field
v-model="newItemText"
label="請輸入名稱"
outlined
dense
clearable
autofocus
@keyup.enter="confirmAddItem"
></v-text-field>
</v-card-text>
<v-card-actions>
<div class="d-flex align-center">
<v-checkbox
class="pt-0"
v-model="noSpace"
label="不加空格"
dense
hide-details
></v-checkbox>
<v-icon class="ml-2 mt-1">mdi-backspace</v-icon>
</div>
<v-spacer></v-spacer>
<v-btn text @click="addDialog = false">取消</v-btn>
<v-btn color="primary" @click="confirmAddItem">確定</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 確認刪除對話框 -->
<v-dialog v-model="deleteDialog" max-width="350">
<v-card>
<v-card-title style="font-size: 1rem;">確認刪除</v-card-title>
<v-card-text>
確定要刪除「<strong>{{ deleteMemberName }}</strong>」嗎?<br>
<span class="red--text">此為手動新增項目,刪除後無法復原。</span>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text @click="deleteDialog = false">取消</v-btn>
<v-btn color="error" @click="doRemove">確定刪除</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-app> </v-app>
</div> </div>
<script> <script>
@@ -286,6 +388,15 @@
return { return {
isAllSelected: false, isAllSelected: false,
dialog: false, dialog: false,
addDialog: false, // 新增項目對話框
addDialogType: '', // 'Y' 或 'N'
newItemText: '', // 新增項目的文字
noSpace: true, // 不加空格 checkbox預設勾選
deleteDialog: false, // 確認刪除對話框
deleteIndex: -1, // 要刪除的 index
deleteType: '', // 'Y' 或 'N'
deleteMemberName: '', // 要刪除的名稱(顯示用)
phrases: [], // 常用片語
familyMembers: [], // 親友名單 familyMembers: [], // 親友名單
tabletItem: {}, // 傳入的資料(信眾/牌位資訊) tabletItem: {}, // 傳入的資料(信眾/牌位資訊)
item: {}, item: {},
@@ -361,6 +472,12 @@
} }
}, },
mounted() { mounted() {
// 載入常用片語
fetch('phrases.json')
.then(res => res.json())
.then(data => { this.phrases = data; })
.catch(err => console.error('載入 phrases.json 失敗:', err));
// 監聽來自父頁面的消息 // 監聽來自父頁面的消息
window.addEventListener('message', (event) => { window.addEventListener('message', (event) => {
this.isAllSelected = false; this.isAllSelected = false;
@@ -452,6 +569,34 @@
console.log(this.family_deceased_N_selected) console.log(this.family_deceased_N_selected)
console.log(this.family_deceased_Y_selected) console.log(this.family_deceased_Y_selected)
}, },
openAddDialog(type) {
this.addDialogType = type;
this.newItemText = '';
this.noSpace = true; // 重置為預設勾選
this.addDialog = true;
},
confirmAddItem() {
if (!this.newItemText.trim()) {
return; // 不允許空白
}
const newItem = {
num: 0,
fam_name: this.newItemText.trim(),
fam_gender: '',
deceased: false,
fam_title: '',
option_break: false,
IsShuWen: this.isAllSelected,
nospace: this.noSpace
};
if (this.addDialogType === 'Y') {
this.family_deceased_Y_selected.push(newItem);
} else if (this.addDialogType === 'N') {
this.family_deceased_N_selected.push(newItem);
}
this.addDialog = false;
this.newItemText = '';
},
addToSelected(member, type) { addToSelected(member, type) {
const selectedMember = { const selectedMember = {
num: member.num, num: member.num,
@@ -471,21 +616,27 @@
console.log(this.family_deceased_N_selected) console.log(this.family_deceased_N_selected)
} }
}, },
removeFromSelected(member, type) { confirmRemove(member, index, type) {
if (member.num === 0) {
// num=0 需確認
this.deleteIndex = index;
this.deleteType = type;
this.deleteMemberName = member.fam_name;
this.deleteDialog = true;
} else {
// num!=0 直接刪除
this.removeFromSelected(index, type);
}
},
doRemove() {
this.removeFromSelected(this.deleteIndex, this.deleteType);
this.deleteDialog = false;
},
removeFromSelected(index, type) {
if (type === 'Y') { if (type === 'Y') {
this.family_deceased_Y_selected.forEach(m => { this.family_deceased_Y_selected.splice(index, 1);
if (m.num === member.num) {
Vue.delete(m, 'IsShuWen');
}
});
this.family_deceased_Y_selected = this.family_deceased_Y_selected.filter(m => m.num !== member.num);
} else if (type === 'N') { } else if (type === 'N') {
this.family_deceased_N_selected.forEach(m => { this.family_deceased_N_selected.splice(index, 1);
if (m.num === member.num) {
Vue.delete(m, 'IsShuWen');
}
});
this.family_deceased_N_selected = this.family_deceased_N_selected.filter(m => m.num !== member.num);
} }
}, },
moveUp(member, type, index) { moveUp(member, type, index) {
@@ -508,23 +659,25 @@
console.log("breakAfter", member, type); console.log("breakAfter", member, type);
member.option_break = !member.option_break; member.option_break = !member.option_break;
}, },
toggleNoSpace(member) {
this.$set(member, 'nospace', !member.nospace);
},
join_text(target) { join_text(target) {
// console.log("join_text", target);
// join each item by ' '
// and for each item, if true for "option_break", then add "<br>" in the item text
let result = ''; let result = '';
target.forEach((member, index) => { target.forEach((member, index) => {
// 每項前面加空格,但例外:
// 1. 第一項前面不加空格 (index === 0)
// 2. nospace === true 的項目前面不加空格
if (index > 0 && !member.nospace) {
result += ' ';
}
result += member.fam_name; result += member.fam_name;
// Add break if option_break is true and not the last item // 處理換行:如果 option_break true 且不是最後一項,加 <br>
if (member.option_break && index < target.length - 1) { if (member.option_break && index < target.length - 1) {
result += '<br>'; result += '<br>';
} else if (index < target.length - 1) {
// Add space between names if not the last item and no break
result += ' ';
} }
}); });
result = result.replace('|', '<br>'); result = result.replace('|', '<br>');
return result; return result;
}, },
calculateTextStyle(text) { calculateTextStyle(text) {

View File

@@ -0,0 +1,13 @@
[
"歷代祖先",
"冤親債主",
"嬰靈",
"地基主",
"無祀孤魂",
"累世父母",
"六親眷屬",
"有緣無緣眾生",
"十方法界眾生",
"水陸空一切眾生"
]