Files
17168ERP/web/admin/region/bed/index.aspx

402 lines
16 KiB
Plaintext
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.
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="admin_region_bed_index" %>
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
<div class="card mb-3 w-100">
<div class="card-body bg-light p-3 rounded" style="height: 150px;">
<div class="d-flex h-100 w-100">
<!-- 左側統計區塊 40% -->
<div class="d-flex gap-2 pe-3" style="flex: 0 0 40%; height: 100%; border-right: 2px solid #ccc;">
<!-- 總房間 -->
<div class="p-2 border rounded text-center flex-fill d-flex flex-column justify-content-center" style="background-color: #f0f8ff;">
<div><strong>總房間</strong></div>
<div>{{ summary?.totalRooms || 0 }}</div>
<div class="text-primary">男: {{ summary?.totalMaleRooms || 0 }}</div>
<div class="text-danger">女: {{ (summary?.totalRooms || 0) - (summary?.totalMaleRooms || 0) }}</div>
</div>
<!-- 總床位 -->
<div class="p-2 border rounded text-center flex-fill d-flex flex-column justify-content-center" style="background-color: #fff0f5;">
<div><strong>總床位</strong></div>
<div>{{ summary?.totalBeds || 0 }}</div>
<div class="text-primary">男: {{ summary?.totalMaleBeds || 0 }}</div>
<div class="text-danger">女: {{ (summary?.totalBeds || 0) - (summary?.totalMaleBeds || 0) }}</div>
</div>
<!-- 可用床位 -->
<div class="p-2 border rounded text-center flex-fill d-flex flex-column justify-content-center" style="background-color: #f5fff0;">
<!-- 標題 -->
<h6 class="mb-2">查詢結果</h6>
<!-- 內容 -->
<div><strong>可用床位</strong></div>
<div class="text-primary">男: {{ filteredStats.maleBeds }}|女: {{ filteredStats.femaleBeds }}</div>
</div>
</div>
<!-- 右側篩選區塊 60% -->
<div class="d-flex gap-3 ms-10" style="flex: 0 0 60%; height: 100%; align-items: flex-start;">
<!-- 分組 1: 區域 & 房間下拉框 -->
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 160px;">
<h6 class="mb-2">區域與房間</h6>
<div class="d-flex flex-column gap-2">
<select class="form-select" v-model="filter.selectedArea">
<option :value=null>全部區域</option>
<option v-for="region in filter.areas" :value="region.uuid" :key="region.uuid">{{region.name}}</option>
</select>
<select class="form-select" v-model="filter.selectedRoom">
<option :value=null>房間</option>
<option v-for="room in filter.rooms" :value="room.uuid" :key="room.uuid">{{room.name}}</option>
</select>
</div>
</div>
<!-- 分組 2: 日期篩選 -->
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 160px;">
<h6 class="mb-2">日期篩選</h6>
<div class="d-flex flex-column gap-2">
<input type="date" class="form-control" v-model="filter.startDate" :min="today" @change="onStartDateChange"/>
<input type="date" class="form-control" v-model="filter.endDate" :min="filter.startDate || today"/>
</div>
</div>
<!-- 分組 3: 占用床位 & 性別復選框 -->
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 200px;">
<h6 class="mb-2">床位與性別</h6>
<!-- 上排 -->
<div class="d-flex gap-3 p-2 mb-2 border rounded shadow-sm" style="background-color: #f8f9fa;">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="occupied" v-model="filter.occupied" @change="toggleOccupied('occupied')">
<label class="form-check-label" for="occupied">已佔用床位</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unoccupied" v-model="filter.unoccupied" @change="toggleOccupied('unoccupied')">
<label class="form-check-label" for="unoccupied">可用床位</label>
</div>
</div>
<!-- 下排 -->
<div class="d-flex gap-3 p-2 border rounded shadow-sm" style="background-color: #f8f9fa;">
<div class="form-check">
<input type="checkbox" id="male" :checked="filter.Gender === true" @change="toggleGender(true)">
<label class="form-check-label" for="male">男</label>
</div>
<div class="form-check">
<input type="checkbox" id="female" :checked="filter.Gender === false" @change="toggleGender(false)">
<label class="form-check-label" for="female">女</label>
</div>
</div>
</div>
<!-- 分組 4: 查詢與清空按鈕 -->
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 120px;">
<h6 class="mb-2">操作</h6>
<div class="d-flex flex-column gap-2">
<button class="btn btn-success" type="button" @click="search">查詢</button>
<button class="btn btn-primary" type="button" @click="clearFilter">清空條件</button>
</div>
</div>
</div>
</div>
</div>
</div>
</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 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">
<div class="card h-100 shadow-sm" style="min-height: 300px; max-height: 400px; overflow-y: auto; border-radius: 0.5rem;">
<!-- 上部:房間名稱 -->
<div class="card-header bg-primary text-white fw-bold">
<h5 class="mb-0">{{ room.name + ' (' + (room.gender ? '男' : '女') + ')' }}</h5>
</div>
<!-- 下部:左右兩列 -->
<div class="card-body p-2">
<div class="row">
<!-- 左列:房間資訊 -->
<div class="col-3 border-end pe-3">
<p class="mb-1"><strong>總床位:</strong>{{ room.stats.bedCount }}</p>
<p class="text-success mb-1"><strong>可用:</strong>{{ room.stats.availableCount }}</p>
<p class="text-danger mb-0"><strong>已占用:</strong>{{ room.stats.bedCount - room.stats.availableCount }}</p>
</div>
<!-- 右列:床位清單 -->
<div class="col-9 ps-3">
<h6 class="fw-bold mb-2">床位清單</h6>
<div style="max-height: 200px; overflow-y: auto;">
<table class="table table-sm table-bordered mb-0">
<thead>
<tr>
<th scope="col">床位名稱</th>
<th scope="col">使用明細</th>
<th scope="col">掛單</th>
<th scope="col">當前狀態</th>
</tr>
</thead>
<tbody>
<tr v-for="bed in room.beds" :key="bed.uuid"
:class="bed.canuse ? 'table-success' : 'table-danger'">
<td>{{ bed.name }}</td>
<td>
<button type="button" class="btn btn-primary" @click="showBedSchedule(bed)">查看明細</button>
</td>
<td>
<button type="button" class="btn btn-primary">
快速掛單
</button>
</td>
<td>
{{bed.statusname}}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<v-dialog v-model="bedSchedule.dialogVisible" max-width="900px">
<v-card
style="min-height:50vh; max-height:80vh; display:flex; flex-direction:column;"
>
<v-card-title>
<span class="text-h6">床位排程明細 - {{ bedSchedule.selectedBed?.name }}</span>
<v-spacer></v-spacer>
<v-btn icon @click="closeBedSchedule"><v-icon>mdi-close</v-icon></v-btn>
</v-card-title>
<!-- 关键改动flex:1 1 auto; min-height:0; overflow-y:auto -->
<v-card-text style="flex:1 1 auto; min-height:0; overflow-y:auto;">
<div style="min-height:0;">
<v-data-table
:headers="bedSchedule.scheduleHeaders"
:items="bedSchedule.selectedBed?.schedules || []"
:items-per-page="9999"
class="elevation-1"
dense
hide-default-footer
>
<template #item.scheduleDate="{item}">
{{ item.scheduledate | timeString('YYYY-MM-DD') }}
</template>
<template #item.actions="{item}">
<a :href="'/admin/guadan/create.aspx?orderId='+item.guaDanOrderNo" class="btn btn-primary">
查看掛單
</a>
</template>
</v-data-table>
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text color="primary" @click="closeBedSchedule">關閉</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
</asp:Content>
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
<style>
.region-block { background:#f8f9fa; padding:15px; margin-bottom:20px; border-radius:8px; }
.region-title { font-size:16px; font-weight:bold; margin-bottom:10px; }
.room-list { display:grid; grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); gap:10px; }
.room-card { background:#fff; border:1px solid #ddd; padding:10px; border-radius:6px; }
.text-green { color: green; }
.text-red { color: red; }
</style>
<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: {
regions: [],
summary: {},
todayStr: new Date().toISOString().split('T')[0],
today: new Date().toISOString().split('T')[0],
filter: {
// 日期篩選
startDate: null,
endDate: null,
Gender: null,
// 區域與房間選擇
selectedArea: null,
selectedRoom: null,
// 佔用床位狀態(互斥)
occupied: false, // 已佔用
unoccupied: false, // 未佔用
areas: [
],
rooms: [
]
},
bedSchedule: {
dialogVisible: false,
selectedBed: null,
scheduleHeaders: [
{ text: '使用日期', value: 'scheduledate' },
{ text: '掛單單號', value: 'guaDanOrderNo' },
{ text: '標題', value: 'title' },
{ text: '掛單人', value: 'usename' },
{ text: '查看掛單', value: 'actions' },
],
},
},
methods: {
showBedSchedule(bed) {
this.bedSchedule.selectedBed = bed;
this.bedSchedule.dialogVisible = true;
console.log(bed)
},
closeBedSchedule() {
this.bedSchedule.selectedBed = null;
this.bedSchedule.dialogVisible = false;
},
search() {
this.GetRegionList();
},
onStartDateChange() {
if (this.filter.endDate && this.filter.endDate < this.filter.startDate) {
this.filter.endDate = this.filter.startDate;
}
if (!this.filter.endDate) {
this.filter.endDate = this.filter.startDate;
}
},
resetFilter() {
this.filter.startDate = null;
this.filter.endDate = null;
this.filter.selectedArea = null;
this.filter.selectedRoom = null;
this.filter.occupied = false;
this.filter.unoccupied = false;
this.filter.Gender = null;
},
clearFilter() {
this.resetFilter();
this.GetRegionList();
},
GetRegionList() {
const payload = {
startDate: this.filter.startDate,
endDate: this.filter.endDate,
areaId: this.filter.selectedArea,
roomId: this.filter.selectedRoom,
occupied: this.filter.occupied,
unoccupied: this.filter.unoccupied,
gender: this.filter.Gender,
};
axios.post('/api/region/list', payload)
.then((res) => {
this.regions = res.data.regions;
this.summary = res.data.summary; // 保存後端統計
})
.catch((err) => {
console.error('API 錯誤', err);
});
},
toggleOccupied(type) {
if (type === "occupied" && this.filter.occupied) {
this.filter.unoccupied = false;
}
if (type === "unoccupied" && this.filter.unoccupied) {
this.filter.occupied = false;
}
},
toggleGender(value) {
// 如果當前選擇就是自己,則取消選擇
this.filter.Gender = this.filter.Gender === value ? null : value;
console.log(this.filter.Gender);
},
getRegionWithRoom() {
axios.get('/api/region/regionwithroom')
.then((res) => {
this.filter.areas = res.data;
})
},
async loadRooms() {
try {
let url = "/api/room/roomwithbed";
const params = {};
if (this.filter.selectedArea) {
params.RegionUuid = this.filter.selectedArea;
}
const res = await axios.get(url, { params });
this.filter.rooms = res.data; // axios 會自動解析 json
this.filter.selectedRoom = null; // 切換區域時,重設房間選擇
} catch (error) {
console.error("載入房間失敗:", error);
}
},
},
watch: {
'filter.selectedArea'() {
this.loadRooms();
}
},
mounted() {
this.GetRegionList();
this.getRegionWithRoom();
this.loadRooms(); // 預設載入所有房間
},
computed: {
filteredStats() {
if (!this.regions || this.regions.length === 0) {
return { maleBeds: 0, femaleBeds: 0 };
}
let maleBeds = 0;
let femaleBeds = 0;
this.regions.forEach(region => {
(region.room || []).forEach(room => {
(room.beds || []).forEach(bed => {
if (bed.canuse) {
if (bed.gender) maleBeds++;
else femaleBeds++;
}
});
});
});
return { maleBeds, femaleBeds };
}
}
})
</script>
</asp:Content>