This commit is contained in:
2025-09-25 15:18:34 +08:00
parent c6bd763485
commit 71490b1fac
14 changed files with 759 additions and 63 deletions

View File

@@ -94,6 +94,10 @@
<template v-slot:item.name="{item}">
{{item.follower?.u_name}}
</template>
<template v-slot:item.sex="{item}">
{{item.follower?.sex}}
</template>
<template #item.actions="{ item }">
<div>
<!-- 取消預訂 -->
@@ -325,13 +329,13 @@
<div v-for="bed in region_modal.currentSelectBeds" :key="bed.uuid" @click="selectBed(bed)" style="padding: 8px; border: 1px solid #d9d9d9; cursor: pointer; max-height: 250px;" :style="{
backgroundColor: region_modal.currentSelectBed?.uuid === bed.uuid
? '#bae7ff' // 當前選中
: (bed.canUsed ? '#f6ffed' : '#fff1f0'), // 可用綠色,不可用紅色
color: bed.canUsed ? 'black' : '#999', // 不可用時灰色文字
pointerEvents: bed.canUsed ? 'auto' : 'none' // 不可用時無法點擊
: ((bed.canUsed && bed.isActive && !bed.bedIsStop) ? '#f6ffed' : '#fff1f0'), // 可用綠色,不可用紅色
color: (bed.canUsed && bed.isActive && !bed.bedIsStop) ? 'black' : '#999', // 不可用時灰色文字
pointerEvents: (bed.canUsed && bed.isActive && !bed.bedIsStop) ? 'auto' : 'none' // 不可用時無法點擊
}">
<div style="font-weight: 500;">{{ bed.name }}</div>
<div style="margin-top: 4px; font-size: 12px;">
{{ bed.canUsed ? '可用' : '不可用' }}
{{ (!bed.isActive || bed.bedIsStop) ? '停用' : (bed.canUsed ? '可用' : '不可用') }}
</div>
<div
@@ -884,6 +888,10 @@
text: '姓名',
value: 'name'
},
{
text: '性別',
value: 'sex'
},
{
text: '掛單開始時間',
value: 'checkinat'
@@ -892,10 +900,6 @@
text: '掛單結束時間',
value: 'checkoutat'
},
{
text: '房間',
value: 'roomName'
},
{
text: '床位',
value: 'bedName'

View File

@@ -51,6 +51,9 @@
<template #item.created_at="{item}">
{{item.created_at | timeString('YYYY/MM/DD HH:mm')}}
</template>
<template #item.activity="{item}">
{{item.activity?.subject}}
</template>
</v-data-table>
<v-container>
<v-row class="align-baseline" wrap="false">
@@ -114,7 +117,7 @@
{ text: '掛單人數', value: 'guest_count' },
{ text: '狀態', value: 'statusName', align: 'center' },
{ text: '建立時間', value: 'created_at', align: 'center' },
{ text: '備註', value: 'notes', align: 'center' },
{ text: '關聯活動', value: 'activity', align: 'center' },
{ text: '操作', value: 'actions', align: 'center' }
],
options: {

View File

@@ -108,8 +108,9 @@
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<div v-for="region in regions" :key="region.uuid" class="region-block mb-4">
<h2 class="region-title mb-3">{{ region.regionPath }}</h2>
<div v-for="region in regions" :key="region.uuid" class="region-block mb-4"
:style="region.isStop ? { pointerEvents: 'none', opacity: '0.6', backgroundColor: '#f8d7da' } : {}">
<h2 class="region-title mb-3">{{region.isStop?(region.regionPath + '(已停用)'): region.regionPath }}</h2>
<div class="row g-3 justify-content-start">
<div v-for="room in region.room" :key="room.ruid" class="col-12 col-md-6 col-lg-6">
@@ -319,6 +320,14 @@
.then((res) => {
this.regions = res.data.regions;
this.summary = res.data.summary; // 保存後端統計
try {
this.regions.sort((a, b) => {
return Number(a.isStop) - Number(b.isStop);
});
}
catch (error) {
console.log(error)
}
})
.catch((err) => {
console.error('API 錯誤', err);

View File

@@ -0,0 +1,217 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="handle_bed_in_used.aspx.cs" Inherits="admin_region_handle_bed_in_used" %>
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
<nav>
<div>
<label style="display: inline-block;">區域:</label>
<select style="display: inline-block; min-width:150px; max-width:20%;"
class="form-select"
v-model="selectedRegionUuid"
:disabled="isFromUrl"
@change="onRegionChange"
><option :value="null">請選擇區域</option>
<option
v-for="region in regions"
:key="region.uuid"
:value="region.uuid"
>
{{ region.name }}
</option>
</select>
<label style="display: inline-block;margin-left: 20px;">客房:</label>
<select style="display: inline-block; min-width:200px; max-width:20%;"
class="form-select"
v-model="selectedRoomUuid"
:disabled="isFromUrl"
@change="onRoomChange"
>
<option :value="null">請選擇房間</option>
<option v-for="room in rooms" :key="room.uuid" :value="room.uuid">{{room.fullName}}</option>
</select>
<label style="display: inline-block;margin-left: 20px;">床位:</label>
<select style="display: inline-block; width:100px;"
class="form-select"
v-model="selectedBedUuid"
:disabled="isFromUrl"
></select>
</div>
</nav>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<div>
<v-data-table
:items="items"
show-select
v-model:selected="selectedItems"
item-key="bedUuid"
:headers="headers">
<template #item.actions="{item}">
<button class="btn btn-outline-danger" type="button" @click="confirmAndCancelSingleBedBooking(item)">取消預約</button>
</template>
<template #item.guadan_during="{item}">
{{item.guadan_during.checkInAt|timeString('YYYY-MM-DD')}} - {{item.guadan_during.checkOutAt|timeString('YYYY-MM-DD')}}
</template>
<template #item.status="{item}">
{{item.status.name}}
</template>
</v-data-table>
</div>
<!-- 更新修改確認彈出視窗 -->
<message-modal ref="messageModal"></message-modal>
<!-- 刪除確認彈出視窗 -->
<confirm-modal ref="confirmModal"></confirm-modal>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
<script>
Vue.filter('timeString', function (value, myFormat) {
return value == null || value == "" ? "" : moment(value).format(myFormat || 'YYYY-MM-DD, HH:mm:ss');
});
new Vue({
el: '#app',
vuetify: new Vuetify(vuetify_options),
data() {
return {
regionUuidFromUrl: '<%= Request.QueryString["region"] %>' || null,
roomUuidFromUrl: '<%= Request.QueryString["room"] %>' || null,
bedUuidFromUrl: '<%= Request.QueryString["bed"] %>' || null,
// 用戶選擇的值
selectedRegionUuid: null,
selectedRoomUuid: null,
selectedBedUuid: null,
regions: [],
rooms: [],
bed: [],
selectedItems: [],
items: [],
headers: [
{ text: '床位名稱', value: 'fullName' },
{ text: '掛單號:', value: 'guaDanOrderNo' },
{ text: '掛單人', value: 'u_name' },
{ text: '掛單時間', value: 'guadan_during' },
{ text: '掛單狀態', value: 'status' },
{ text: '', value: 'actions' },
]
}
},
methods: {
onRegionChange() {
console.log("選擇的區域 UUID:", this.selectedRegionUuid);
this.selectedRoomUuid = null;
this.selectedBedUuid = null;
this.GetInUsedBed();
this.GetRoomList();
},
onRoomChange() {
console.log("選擇的客房 UUID:", this.selectedRoomUuid);
this.GetInUsedBed();
},
async GetInUsedBed() {
//獲取已經預約或者入住的床位
try {
const payload = {
regionUuid: this.selectedRegionUuid || null,
roomUuid: this.selectedRoomUuid || null,
bedUuid: this.selectedBedUuid || null
};
const response = await axios.post(HTTP_HOST + "api/bed/inuse/list", payload);
// 假設返回的就是床位數組
this.items = response.data;
console.log("已獲取床位:");
} catch (error) {
console.error("獲取床位失敗:", error);
}
},
async confirmAndCancelSingleBedBooking(item) {
// 先彈出確認彈出視窗
this.$refs.confirmModal.open({
message: `確定要取消床位 ${item.name || ''} 的所有預約嗎?`,
onConfirm: async () => {
try {
const payload = {
bedUuid: item.bedUuid || null
};
const response = await axios.post(
HTTP_HOST + "api/bed/inuse/cancel/singlebed/booking",
payload
);
// 刷新床位列表
this.GetInUsedBed();
// 成功提示
console.log("取消成功:", item.bedUuid);
this.$refs.messageModal.open({
title: '取消成功',
message: response?.data?.message || '取消成功!',
status: 'success'
});
} catch (error) {
console.error("取消失敗:", error);
this.$refs.messageModal.open({
title: '取消失敗',
message: error.response?.data?.message || '取消過程中發生錯誤!',
status: 'error'
});
}
}
});
},
async CancelAllBedBooking() {
//取消符合條件的所有床位的所有預約
},
async GetRegionList() {
try {
const response = await axios.get(
HTTP_HOST + "api/bed/inuse/region/list"
);
this.regions = response.data;
} catch (error) {
}
},
async GetRoomList() {
try {
const response = await axios.get(
HTTP_HOST + "api/bed/inuse/room/list", {
params: {
regionUuid: this.selectedRegionUuid
}
}
);
this.rooms = response.data;
} catch (error) {
}
},
},
watch: {
},
mounted() {
this.selectedRegionUuid = this.regionUuidFromUrl;
this.selectedRoomUuid = this.roomUuidFromUrl;
this.selectedBedUuid = this.bedUuidFromUrl;
this.GetInUsedBed();
this.GetRegionList();
this.GetRoomList();
},
computed: {
// 判斷是否來自 URL禁用下拉框
isFromUrl() {
return this.regionUuidFromUrl || this.roomUuidFromUrl || this.bedUuidFromUrl;
}
}
});
</script>
</asp:Content>

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class admin_region_handle_bed_in_used : MyWeb.config
{
protected void Page_Load(object sender, EventArgs e)
{
}
}

View File

@@ -252,7 +252,7 @@
<div class="mb-3">
<label class="form-label">狀態</label>
<select class="form-control" v-model="room_bed.newBedForm.statuscode">
<select class="form-control" v-model="room_bed.newBedForm.statuscode" disabled>
<option v-for="status in room_bed.bed_status" :value="status.code">
{{status.name}}
</option>
@@ -349,6 +349,23 @@
</v-card-actions>
</v-card>
</v-dialog>
<!-- 停用區域如果有床位正在掛單提示彈出視窗 -->
<div v-if="bed_is_used_modal" style="position:fixed; top:0; left:0; width:100%; height:100%;
background:rgba(0,0,0,0.5); display:flex; align-items:center; justify-content:center; z-index:9999;">
<div style="background:#fff; padding:20px; border-radius:8px; width:300px; text-align:center;">
<p style="margin-bottom:15px;">{{bed_is_used_modal_message}}</p>
<div style="display:flex; justify-content:flex-end; gap:10px;">
<button @click="closeBedInUsedModal"
class="btn btn-danger"
type="button" style="padding:6px 12px; border:none; border-radius:4px; cursor:pointer;">关闭</button>
<a :href="bed_is_used_modal_link" target="_blank" class="btn btn-primary">
前往处理
</a>
</div>
</div>
</div>
<!-- 更新修改確認彈出視窗 -->
<message-modal ref="messageModal"></message-modal>
<!-- 刪除確認彈出視窗 -->
@@ -477,13 +494,14 @@
</div>
`
});
new Vue({
el: '#app',
vuetify: new Vuetify(vuetify_options),
data() {
return {
bed_is_used_modal: false,
bed_is_used_modal_message: null,
bed_is_used_modal_link: "handle_bed_in_used.aspx", // 默认链接
selectedId: null, // 被選中項目ID
selectedType: null, // 'region' 或 'room'
expandAllFlag: false, // 控制全部展開
@@ -546,6 +564,11 @@
};
},
methods: {
closeBedInUsedModal() {
this.bed_is_used_modal = false;
this.bed_is_used_modal_message = null;
this.bed_is_used_modal_link = "handle_bed_in_used.aspx"; // 默认链接
},
expandAll() {
this.expandAllFlag = true;
this.collapseAllFlag = false;
@@ -634,7 +657,10 @@
});
return;
}
const url = this.form.uuid ? '/api/region/update' : '/api/region/create';
const url = this.form.uuid
? HTTP_HOST + '/api/region/update'
: HTTP_HOST + '/api/region/create';
axios.post(url, this.form)
.then((res) => {
//alert('儲存成功');
@@ -649,10 +675,20 @@
});
})
.catch((error) => {
this.$refs.messageModal.open({
title: '更新提示',
message: error.response?.data?.message || "儲存失敗,請稍後再試。",
});
console.error('更新失敗', error);
const code = error.response?.data?.code;
const message = error.response?.data?.message || error.message ||
"未知錯誤,請稍後再試";
if (code === "BED_IS_USED") {
this.bed_is_used_modal = true;
this.bed_is_used_modal_message = message
this.bed_is_used_modal_link = this.bed_is_used_modal_link + '?region=' + this.form.uuid
} else {
this.$refs.messageModal.open({
message: (message)
});
}
});
},
deleteRegion() {
@@ -665,7 +701,7 @@
});
},
confirmDeleteRegion() {
axios.post('/api/region/delete', { Uuid: this.form.uuid })
axios.post(HTTP_HOST + 'api/region/delete', { Uuid: this.form.uuid })
.then(() => {
this.showDeleteModal = false;
this.$refs.messageModal.open({
@@ -717,7 +753,7 @@
uuid: null,
RoomUuid: this.currentSelectRoom.uuid,
Name: '',
statuscode: null,
statuscode: "101",
IsActive: true,
Gender: this.currentSelectRoom.gender, // 不設預設值,強制選擇
};
@@ -732,7 +768,7 @@
return;
}
try {
var res = await axios.post('/api/region/bed/create', this.room_bed.newBedForm);
var res = await axios.post(HTTP_HOST + 'api/region/bed/create', this.room_bed.newBedForm);
this.room_bed.showBedModal = false;
this.$refs.messageModal.open({
title: '成功',
@@ -825,12 +861,20 @@
await this.loadRegions();
this.room_bed.bed_items = this.currentSelectRoom.beds;
//this.selectRegion(this.findRegionById(this.regions, this.form.id));
} catch (err) {
console.log(err)
this.$refs.messageModal.open({
title: '錯誤',
message: err.response?.data?.message || '更新失敗'
});
} catch (error) {
console.error('更新失敗', error);
const code = error.response?.data?.code;
const message = error.response?.data?.message || error.message ||
"未知錯誤,請稍後再試";
if (code === "BED_IS_USED") {
this.bed_is_used_modal = true;
this.bed_is_used_modal_message = message;
this.bed_is_used_modal_link = this.bed_is_used_modal_link + '?bed=' + this.room_bed.newBedForm.uuid
} else {
this.$refs.messageModal.open({
message: (message)
});
}
}
},
getBedStatus() {
@@ -887,9 +931,18 @@
};
} catch (error) {
console.error('更新失敗', error);
this.$refs.messageModal.open({
message: (error.response?.data?.message || error.message)
});
const code = error.response?.data?.code;
const message = error.response?.data?.message || error.message ||
"未知錯誤,請稍後再試";
if (code === "BED_IS_USED") {
this.bed_is_used_modal = true;
this.bed_is_used_modal_message = message;
this.bed_is_used_modal_link = this.bed_is_used_modal_link + '?room=' + this.room.room_form.uuid
} else {
this.$refs.messageModal.open({
message: (message)
});
}
}
},