掛單管理
This commit is contained in:
1 parent
8db6019ba3
commit
9e251fc7d4
12 files changed
+2197
-116
No files matched your search
+248
-32
@@ -118,7 +118,7 @@
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-icon color="error" v-bind="attrs" v-on="on" size="20">mdi-alert-circle</v-icon>
|
||||
</template>
|
||||
<span>特殊需求: {{ item.specialNeeds }}</span>
|
||||
<span>特殊需求: {{ getSpecialNeedsText(item.specialNeeds) }}</span>
|
||||
</v-tooltip>
|
||||
<v-icon v-else color="grey lighten-3" size="20">mdi-alert-circle-outline</v-icon>
|
||||
</div>
|
||||
@@ -138,6 +138,10 @@
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item :disabled="item.statuscode !== '401'" @click="confirmRemoveGuadanOrderGuest(item)">
|
||||
<v-list-item-icon class="me-2"><v-icon color="error" size="20">mdi-trash</v-icon></v-list-item-icon>
|
||||
<v-list-item-title class="error--text">刪除</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item :disabled="item.statuscode !== '401'" @click="confirmDeleteGuadanOrderGuest(item)">
|
||||
<v-list-item-icon class="me-2"><v-icon color="error" size="20">mdi-cancel</v-icon></v-list-item-icon>
|
||||
<v-list-item-title class="error--text">取消預訂</v-list-item-title>
|
||||
@@ -197,18 +201,36 @@
|
||||
{{ room.name }} (剩餘: {{ roomAvailableBedsCount(room) }})
|
||||
</div>
|
||||
|
||||
<!-- 床位列表 -->
|
||||
<!-- 床位列表 (加入 Tooltip 提示特殊條件) -->
|
||||
<v-chip-group column>
|
||||
<v-chip v-for="bed in room.beds"
|
||||
:key="bed.id"
|
||||
:color="bed.id === selectedGuestForBed.bedUuid ? 'success' : (bed.isAvailableNow ? 'primary' : 'grey')"
|
||||
:text-color="bed.id === selectedGuestForBed.bedUuid || bed.isAvailableNow ? 'white' : 'black'"
|
||||
:disabled="!bed.isAvailableNow && bed.id !== selectedGuestForBed.bedUuid"
|
||||
@click="assignBedToGuest(building, room, bed)"
|
||||
small>
|
||||
{{ bed.name }}
|
||||
<v-icon v-if="bed.id === selectedGuestForBed.bedUuid" right size="14">mdi-check-circle</v-icon>
|
||||
</v-chip>
|
||||
<template v-for="bed in room.beds">
|
||||
<!-- 封裝 Tooltip:有條件時提示內容,沒條件時提示無特殊限制 -->
|
||||
<v-tooltip bottom :key="bed.id">
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-chip :color="bed.id === selectedGuestForBed.bedUuid ? 'success' : (bed.isAvailableNow ? 'primary' : 'grey')"
|
||||
:text-color="bed.id === selectedGuestForBed.bedUuid || bed.isAvailableNow ? 'white' : 'black'"
|
||||
:disabled="!bed.isAvailableNow && bed.id !== selectedGuestForBed.bedUuid"
|
||||
@click="assignBedToGuest(building, room, bed)"
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
small>
|
||||
{{ bed.name }}
|
||||
<v-icon v-if="bed.id === selectedGuestForBed.bedUuid" right size="14">mdi-check-circle</v-icon>
|
||||
<!-- 若床位有特殊條件,加上小圖示標示 -->
|
||||
<v-icon v-if="bed.limits && bed.limits.length > 0" right size="12" color="amber lighten-3">mdi-star</v-icon>
|
||||
</v-chip>
|
||||
</template>
|
||||
<span>
|
||||
{{ bed.name }}
|
||||
<template v-if="bed.limits && bed.limits.length > 0">
|
||||
<br>【具備條件】:{{ getSpecialNeedsText(Array.isArray(bed.limits) ? bed.limits.join(',') : String(bed.limits)) }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<br>(無特殊限制床位)
|
||||
</template>
|
||||
</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-chip-group>
|
||||
|
||||
<v-divider v-if="index < building.rooms.length - 1" class="mt-2"></v-divider>
|
||||
@@ -277,7 +299,7 @@
|
||||
<v-select :items="e2" v-model="e5" label="性別"></v-select>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-select v-model="e6" :items="states" label="特殊需求" multiple hint="選擇條件" persistent-hint></v-select>
|
||||
<v-select v-model="e6" :items="states" item-text="text" item-value="value" label="特殊需求" multiple hint="選擇條件" persistent-hint></v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
@@ -391,20 +413,28 @@
|
||||
|
||||
const guestGender = this.selectedGuestForBed.gender;
|
||||
const currentGuestBedUuid = this.selectedGuestForBed.bedUuid;
|
||||
|
||||
const guestNeeds = (this.selectedGuestForBed.specialNeeds || '').split(',').filter(c => c);
|
||||
return this.mockBuildings.map(building => {
|
||||
console.log('filteredMockBuildings:', building);
|
||||
// 過濾房間並處理床位動態狀態
|
||||
const filteredRooms = building.rooms.map(room => {
|
||||
return {
|
||||
...room,
|
||||
beds: room.beds.map(bed => {
|
||||
const isTakenByOthers = this.assignedBedUuids.includes(bed.id) && bed.id !== currentGuestBedUuid;
|
||||
// 檢查條件是否完全滿足
|
||||
const bedConditions = bed.limits || [];
|
||||
const isMatchConditions = guestNeeds.every(need => bedConditions.includes(need));
|
||||
room["regionPath"] = bed.regionPath;
|
||||
|
||||
return {
|
||||
...bed,
|
||||
isAvailableNow: bed.isAvailable && !isTakenByOthers
|
||||
isAvailableNow: bed.isAvailable && !isTakenByOthers && isMatchConditions,
|
||||
isMatchConditions: isMatchConditions
|
||||
};
|
||||
})
|
||||
};
|
||||
console.log("filteredMockBuildings: room:", room);
|
||||
}).filter(room =>
|
||||
!guestGender || guestGender === '請選擇' || room.genderLimit === guestGender
|
||||
);
|
||||
@@ -438,6 +468,16 @@
|
||||
roomAvailableBedsCount(room) {
|
||||
return room.beds.filter(b => b.isAvailableNow).length;
|
||||
},
|
||||
getSpecialNeedsText(specialNeedsCodes) {
|
||||
if (!specialNeedsCodes) return '';
|
||||
const codes = specialNeedsCodes.split(',');
|
||||
let self = this;
|
||||
const names = codes.map(code => {
|
||||
const target = self.states.find(s => s.value === code);
|
||||
return target ? target.text : code;
|
||||
});
|
||||
return names.join('、');
|
||||
},
|
||||
getGuadanLimits() {//抓掛單條件
|
||||
this.states.length = 0; // 清空陣列
|
||||
let self = this;
|
||||
@@ -445,8 +485,11 @@
|
||||
axios.post(HTTP_HOST + 'api/guadan/getGuadanLimit').then((res) => {
|
||||
if (res.status == 200) {
|
||||
res.data.forEach(item => {
|
||||
console.log('掛單條件', item);
|
||||
self.states.push(item.paramName);
|
||||
|
||||
self.states.push({
|
||||
text: item.paramName,
|
||||
value: item.paramNo
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
@@ -457,7 +500,18 @@
|
||||
if (!this.e5 || this.e5 == 0) { console.log("請選擇男眾或女眾"); return; }
|
||||
if (!this.s_date) { console.log('請選擇開始日期'); return; }
|
||||
if (!this.e_date) { console.log('請選擇結束日期'); return; }
|
||||
|
||||
const today = moment().format('YYYY-MM-DD');
|
||||
if (this.s_date < today) {
|
||||
this.message = '開始日期不可小於今天';
|
||||
this.message_dialog = true;
|
||||
return;
|
||||
}
|
||||
// 檢查 2:結束日期不可小於開始日期
|
||||
if (this.e_date < this.s_date) {
|
||||
this.message = '結束日期不可小於開始日期';
|
||||
this.message_dialog = true;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < this.e4; i++) {
|
||||
const tempUuid = 'temp-' + Date.now() + '-' + i;
|
||||
this.guadanguest.items.push({
|
||||
@@ -495,8 +549,40 @@
|
||||
validateOrderForm() {
|
||||
if (!this.guadanorder.order_form.startdate) { this.message = '請輸入開始日期'; this.message_dialog = true; return false; }
|
||||
if (!this.guadanorder.order_form.enddate) { this.message = '請輸入結束日期'; this.message_dialog = true; return false; }
|
||||
const startDate = this.guadanorder.order_form.startdate;
|
||||
const endDate = this.guadanorder.order_form.enddate;
|
||||
// 取得今天日期字串 (YYYY-MM-DD)
|
||||
const today = moment().format('YYYY-MM-DD');
|
||||
|
||||
// 檢查 1:預約開始日期不可小於今天
|
||||
if (startDate < today) {
|
||||
this.message = '預約開始日期不可小於今天';
|
||||
this.message_dialog = true;
|
||||
return false;
|
||||
}
|
||||
// 檢查 2:預約結束日期不可小於預約開始日期
|
||||
if (endDate < startDate) {
|
||||
this.message = '預約結束日期不可小於預約開始日期';
|
||||
this.message_dialog = true;
|
||||
return false;
|
||||
}
|
||||
if (!this.guadanorder.order_form.bookerName) { this.message = '請輸入姓名'; this.message_dialog = true; return false; }
|
||||
if (!this.guadanorder.order_form.bookerPhone) { this.message = '請輸入電話'; this.message_dialog = true; return false; }
|
||||
// 檢查 3:若有掛單明細,檢查是否有「未分配床位」的人員
|
||||
if (this.guadanguest.items && this.guadanguest.items.length > 0) {
|
||||
// 找出沒有 bedUuid 的人員
|
||||
const unassignedGuests = this.guadanguest.items.filter(item => !item.bedUuid);
|
||||
|
||||
if (unassignedGuests.length > 0) {
|
||||
// 找出第一個未分配人員的顯示名稱或順位號碼
|
||||
const firstUnassignedIdx = this.guadanguest.items.indexOf(unassignedGuests[0]) + 1;
|
||||
const guestIdentifier = unassignedGuests[0].fullName || `第 ${firstUnassignedIdx} 位預訂人`;
|
||||
|
||||
this.message = `無法存檔!尚有 ${unassignedGuests.length} 位人員未分配床位(例如:${guestIdentifier})。請先完成床位分配再存檔!`;
|
||||
this.message_dialog = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -522,7 +608,6 @@
|
||||
|
||||
let detail = []
|
||||
this.guadanguest.items.forEach(item => {
|
||||
console.log('明細', item);
|
||||
detail.push({
|
||||
uuid: item.uuid,
|
||||
fullName: item.fullName,
|
||||
@@ -534,10 +619,12 @@
|
||||
note: item.note,
|
||||
handi: item.specialNeeds,
|
||||
bedUuid: item.bedUuid,
|
||||
roomUuid: item.roomUuid,
|
||||
name: item.fullName
|
||||
})
|
||||
console.log('掛單明細', detail);
|
||||
})
|
||||
console.log('掛單主檔', this.guadanorder.order_form);
|
||||
//console.log('掛單主檔', this.guadanorder.order_form);
|
||||
if (!this.validateOrderForm()) return;
|
||||
axios.post(HTTP_HOST + 'api/guadan/saveGandan', { order: this.guadanorder.order_form, detail: detail })
|
||||
.then((res => {
|
||||
@@ -564,14 +651,94 @@
|
||||
this.checkInGuest.showSelectGuadanOrderGuest = true;
|
||||
},
|
||||
|
||||
showAutomaticBedAllocation() {
|
||||
if (!this.guadanorder.order_form.uuid) {
|
||||
async showAutomaticBedAllocation() {
|
||||
console.log(this.guadanguest.items);
|
||||
// 1. 篩選出尚未分配床位的人員
|
||||
const unassignedGuests = this.guadanguest.items.filter(item => !item.bedUuid);
|
||||
|
||||
if (unassignedGuests.length === 0) {
|
||||
this.message = '目前所有人員皆已完成床位分配!';
|
||||
this.message_dialog = true;
|
||||
this.message = '請先儲存掛單資訊';
|
||||
return;
|
||||
}
|
||||
this.automaticBedAllocation.showModal = true;
|
||||
this.getavailablebedcountbytime(this.guadanorder.order_form.startdate, this.guadanorder.order_form.enddate);
|
||||
unassignedGuests.sort((a, b) => {
|
||||
const needsA = (a.specialNeeds || '').split(',').filter(c => c && c !== '無特殊需求').length;
|
||||
const needsB = (b.specialNeeds || '').split(',').filter(c => c && c !== '無特殊需求').length;
|
||||
return needsB - needsA; // 數量多的排在前面 (降冪)
|
||||
});
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
// 2. 逐一處理未分配床位的人員
|
||||
for (const guest of unassignedGuests) {
|
||||
try {
|
||||
// 取得該人員入住時間區間的可分配床位
|
||||
const resp = await axios.post(HTTP_HOST + 'api/region/bed/getAvailableBeds', {
|
||||
startTime: guest.checkinat,
|
||||
endTime: guest.checkoutat
|
||||
});
|
||||
|
||||
const availableBeds = resp.data?.availableBeds || [];
|
||||
// 過濾出真正的特殊需求 (排除「無特殊需求」與空字串)
|
||||
const rawNeeds = (guest.specialNeeds || '').split(',');
|
||||
const guestNeeds = rawNeeds.filter(need => need && need !== '無特殊需求');
|
||||
|
||||
// 3. 搜尋合適的床位
|
||||
const matchedBed = availableBeds.find(bed => {
|
||||
// A. 床位狀態需為可用 ('101')
|
||||
if (bed.statusCode !== '101') return false;
|
||||
|
||||
// B. 不能已被目前列表中的其他人佔用
|
||||
if (this.assignedBedUuids.includes(bed.uuid)) return false;
|
||||
|
||||
// C. 性別限制過濾
|
||||
const regionName = bed.room?.regionName || '';
|
||||
let roomGenderLimit = null;
|
||||
if (regionName.includes('男眾')) roomGenderLimit = '男眾';
|
||||
else if (regionName.includes('女眾')) roomGenderLimit = '女眾';
|
||||
|
||||
if (guest.gender && guest.gender !== '請選擇' && roomGenderLimit) {
|
||||
if (guest.gender !== roomGenderLimit) return false;
|
||||
}
|
||||
|
||||
// D. 特殊需求條件檢查 (人員需要的,床位必須具備)
|
||||
const bedConditions = bed.limits || [];
|
||||
const isMatchConditions = guestNeeds.every(need => bedConditions.includes(need));
|
||||
if (!isMatchConditions) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// 4. 若找到合適床位,進行分配
|
||||
if (matchedBed) {
|
||||
const buildingName = matchedBed.room?.regionName || '';
|
||||
const roomName = matchedBed.room?.name || '';
|
||||
const bedName = matchedBed.name || '';
|
||||
|
||||
this.$set(guest, 'bedName', `${buildingName} - ${roomName} - ${bedName}`);
|
||||
this.$set(guest, 'bedUuid', matchedBed.uuid);
|
||||
this.$set(guest, 'roomUuid', matchedBed.roomUuid);
|
||||
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('自動分配床位發生錯誤:', error);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 分配完成提示
|
||||
this.message = `自動分配完成!成功分配 ${successCount} 位,失敗/無合適床位 ${failCount} 位。`;
|
||||
this.message_dialog = true;
|
||||
//if (!this.guadanorder.order_form.uuid) {
|
||||
// this.message_dialog = true;
|
||||
// this.message = '請先儲存掛單資訊';
|
||||
// return;
|
||||
//}
|
||||
//this.automaticBedAllocation.showModal = true;
|
||||
//this.getavailablebedcountbytime(this.guadanorder.order_form.startdate, this.guadanorder.order_form.enddate);
|
||||
},
|
||||
|
||||
getavailablebedcountbytime(startTime, endTime) {
|
||||
@@ -591,9 +758,56 @@
|
||||
assignBedToGuest(building, room, bed) {
|
||||
if (!bed.isAvailableNow && bed.id !== this.selectedGuestForBed.bedUuid) return;
|
||||
|
||||
// 1. 安全轉換函數:強制將各種型別轉換為標準陣列
|
||||
const ensureArray = (val) => {
|
||||
if (!val) return [];
|
||||
if (Array.isArray(val)) return val;
|
||||
if (typeof val === 'string') return val.split(',').filter(c => c && c.trim() !== '');
|
||||
if (typeof val === 'object') return Object.values(val).filter(c => c);
|
||||
return [String(val)];
|
||||
};
|
||||
|
||||
// 取出安全的床位條件陣列
|
||||
const bedConditions = ensureArray(bed.limits);
|
||||
// 2. 條件比對檢查 (檢核特殊需求)
|
||||
const guestNeedsStr = this.selectedGuestForBed.specialNeeds || '';
|
||||
// 將人員需求轉換為代碼陣列 (過濾空值)
|
||||
const guestNeeds = guestNeedsStr ? guestNeedsStr.split(',').filter(c => c) : [];
|
||||
// 床位條件 (若後端未傳回則預設為空陣列)
|
||||
//const bedConditions = bed.limits || [];
|
||||
|
||||
// 檢查人員有的需求,床位是否未滿足 (找出不合適的條件代碼)
|
||||
const unmatchedNeeds = guestNeeds.filter(need => !bedConditions.includes(need));
|
||||
|
||||
if (unmatchedNeeds.length > 0) {
|
||||
// 將未滿足的需求代碼轉換為中文名稱
|
||||
const unmatchedNames = unmatchedNeeds.map(code => {
|
||||
const target = this.states.find(s => s.value === code);
|
||||
return target ? target.text : code;
|
||||
}).join('、');
|
||||
|
||||
// 跳出提示阻擋分配(或改為 confirm 提醒)
|
||||
this.message = `不符合分配條件!該人員需要「${unmatchedNames}」,但此床位未具備對應條件。`;
|
||||
this.message_dialog = true;
|
||||
return; // 中斷分配
|
||||
}
|
||||
// 將大樓名稱也組進去顯示
|
||||
// 2. 取得床位具備的特殊條件文字
|
||||
let bedConditionNotice = '';
|
||||
if (bedConditions.length > 0) {
|
||||
const bedConditionNames = bedConditions.map(code => {
|
||||
const target = this.states.find(s => s.value === code);
|
||||
return target ? target.text : code;
|
||||
}).join('、');
|
||||
bedConditionNotice = `\n【床位具備條件】:${bedConditionNames}`;
|
||||
} else {
|
||||
bedConditionNotice = `\n【床位具備條件】:無特殊限制`;
|
||||
}
|
||||
console.log(room, bed);
|
||||
// 3. 設定床位並跳出點擊後的二次提示
|
||||
this.$set(this.selectedGuestForBed, 'bedName', `${building.name} - ${room.name} - ${bed.name}`);
|
||||
this.$set(this.selectedGuestForBed, 'bedUuid', bed.id);
|
||||
this.$set(this.selectedGuestForBed, 'roomUuid', room.id);
|
||||
|
||||
this.message = `已將「${building.name} ${room.name} ${bed.name}」分配給該明細!`;
|
||||
this.message_dialog = true;
|
||||
@@ -610,7 +824,6 @@
|
||||
},
|
||||
|
||||
async selectGuestForBed(item) {
|
||||
console.log('選床', item);
|
||||
axios.post(HTTP_HOST + 'api/region/bed/getAvailableBeds', {
|
||||
startTime: item.checkinat, endTime: item.checkoutat
|
||||
}).then((resp) => {
|
||||
@@ -623,7 +836,8 @@
|
||||
const regionName = bed.room.regionName;
|
||||
const roomId = bed.roomUuid;
|
||||
const roomName = bed.room.name;
|
||||
|
||||
const regionPath = bed.regionPath;
|
||||
console.log('床位資料', bed);
|
||||
// 自動判斷性別限制 (若區域名稱包含"男眾"則為"男眾",反之女眾)
|
||||
const genderLimit = regionName.includes('男眾') ? '男眾' : '女眾';
|
||||
|
||||
@@ -653,7 +867,9 @@
|
||||
acc[regionId].roomsMap[roomId].beds.push({
|
||||
id: bed.uuid,
|
||||
name: bed.name,
|
||||
isAvailable: isAvailable
|
||||
isAvailable: isAvailable,
|
||||
limits: bed.limits || [],
|
||||
regionPath: regionPath
|
||||
});
|
||||
|
||||
return acc;
|
||||
@@ -669,15 +885,15 @@
|
||||
|
||||
// 執行轉換
|
||||
const result = transformData(rawData);
|
||||
//console.log(JSON.stringify(result, null, 4));
|
||||
|
||||
|
||||
this.mockBuildings = result;
|
||||
this.selectedGuestForBed = item;
|
||||
this.activeRoomPanel = null;
|
||||
})
|
||||
},
|
||||
|
||||
confirmRemoveGuadanOrderGuest(item) {
|
||||
//let temp = this.guadanguest.items.find(x => x.uuid == item.uuid);
|
||||
this.guadanguest.items = this.guadanguest.items.filter(x => x.uuid != item.uuid);
|
||||
},
|
||||
confirmDeleteGuadanOrderGuest(item) {
|
||||
console.log('取消預訂', item);
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user