539 lines
22 KiB
Plaintext
539 lines
22 KiB
Plaintext
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="view.aspx.cs" Inherits="admin_guadan_view" %>
|
||
|
||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||
</asp:Content>
|
||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||
</asp:Content>
|
||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||
<fieldset class="border rounded p-4 mb-5 shadow-sm bg-white">
|
||
<legend class="w-auto px-3 font-weight-bold text-primary">掛單資訊</legend>
|
||
<!-- 🟢 區塊一:掛單資訊 -->
|
||
<div class="border rounded p-3 bg-white shadow-sm" style="pointer-events: none; user-select: none; background: #1c5bd9; padding: 5px;">
|
||
<h6 class="text-secondary mb-3">📝掛單資訊</h6>
|
||
<div class="form-group row mt-3">
|
||
<label class="col-sm-2 col-form-label text-center">掛單單號(不可修改)</label>
|
||
<div class="col-sm-4 text-left">
|
||
<input class="form-control" v-model="guadanorder.order_form.orderNo" readonly />
|
||
</div>
|
||
<label class="col-sm-2 col-form-label text-center">關聯活動</label>
|
||
<div class="col-sm-4">
|
||
<select class="form-control" v-model="guadanorder.order_form.activityNum" >
|
||
<option :value="null">未關聯</option>
|
||
<option v-for="activity in activityList" :key="activity.num" :value="activity.num">
|
||
{{activity.subject}}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-group row mt-3">
|
||
<label class="col-sm-2 col-form-label text-center">
|
||
預約開始日期
|
||
</label>
|
||
<div class="col-sm-4 text-left">
|
||
<input class="form-control" type="date" v-model="guadanorder.order_form.startdate" />
|
||
</div>
|
||
<label class="col-sm-2 col-form-label text-center">
|
||
預約結束日期
|
||
</label>
|
||
<div class="col-sm-4">
|
||
<input class="form-control" type="date" v-model="guadanorder.order_form.enddate" />
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group row mt-3">
|
||
<label class="col-sm-2 col-form-label text-center">預定人姓名</label>
|
||
<div class="col-sm-4">
|
||
<input class="form-control" v-model="guadanorder.order_form.bookerName" />
|
||
</div>
|
||
<label class="col-sm-2 col-form-label text-center">預定人電話</label>
|
||
<div class="col-sm-4">
|
||
<input class="form-control" v-model="guadanorder.order_form.bookerPhone" />
|
||
</div>
|
||
</div>
|
||
<div class="form-group row mt-3">
|
||
<label class="col-sm-2 col-form-label text-center">備註</label>
|
||
<div class="col-sm-4">
|
||
<textarea class="form-control" v-model="guadanorder.order_form.note"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</fieldset>
|
||
<fieldset class="border rounded p-4 mb-5 shadow-sm bg-white">
|
||
<!-- 表格標題緊貼表格上方,居中 -->
|
||
<div class="d-flex align-items-center mb-3">
|
||
<!-- 中間標題(flex-grow撐開居中) -->
|
||
<div class="flex-grow-1 text-center">
|
||
<h5 class="text-primary fw-bold mb-0">
|
||
<i class="bi bi-people-fill me-2"></i>掛單蓮友
|
||
</h5>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- v-data-table 表格 -->
|
||
<v-data-table :headers="guadanguest.headers" :items="guadanguest.items" class="elevation-1 rounded" dense>
|
||
<template #item.checkinat="{item}">
|
||
{{item.checkinat |timeString('YYYY-MM-DD')}}
|
||
</template>
|
||
<template #item.checkoutat="{item}">
|
||
{{item.checkoutat |timeString('YYYY-MM-DD')}}
|
||
</template>
|
||
<template v-slot:item.name="{item}">
|
||
{{item.follower?.u_name}}
|
||
</template>
|
||
<template v-slot:item.sex="{item}">
|
||
{{item.follower?.sex}}
|
||
</template>
|
||
</v-data-table>
|
||
</fieldset>
|
||
</asp:Content>
|
||
|
||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||
</asp:Content>
|
||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||
<style>
|
||
/* 調整 fieldset 風格 */
|
||
fieldset {
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 0.5rem;
|
||
background-color: #fff;
|
||
}
|
||
|
||
legend {
|
||
font-size: 1.25rem;
|
||
font-weight: 700;
|
||
color: #0d6efd;
|
||
width: auto;
|
||
padding: 0 0.75rem;
|
||
}
|
||
|
||
.form-group label {
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 按鈕置右 */
|
||
.text-right {
|
||
text-align: right;
|
||
}
|
||
|
||
/* 選擇床位相關 */
|
||
.tree,
|
||
.tree ul {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding-left: 1rem;
|
||
}
|
||
|
||
.toggle-icon {
|
||
cursor: pointer;
|
||
user-select: none;
|
||
width: 1rem;
|
||
display: inline-block;
|
||
color: #007bff;
|
||
}
|
||
|
||
.region-item-label {
|
||
cursor: pointer;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
display: inline-block;
|
||
}
|
||
|
||
.region-item-label.selected {
|
||
background-color: #eaf4ff;
|
||
color: #0d6efd;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.selected-room {
|
||
background-color: #cce5ff;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 選擇床位相關 */
|
||
</style>
|
||
<script>
|
||
Vue.component('region-item', {
|
||
props: ['item', 'selectedId', 'selectedType', 'expandAll', 'collapseAll'],
|
||
data() {
|
||
return {
|
||
expanded: false, // 預設全部收起
|
||
selectedRoomId: null,
|
||
}
|
||
},
|
||
watch: {
|
||
expandAll(newVal) {
|
||
if (newVal) {
|
||
this.expanded = true;
|
||
// 執行完後發事件通知父組件清除標誌
|
||
this.$nextTick(() => this.$emit('clear-expand-all'));
|
||
}
|
||
},
|
||
collapseAll(newVal) {
|
||
if (newVal) {
|
||
this.expanded = false;
|
||
this.$nextTick(() => this.$emit('clear-collapse-all'));
|
||
}
|
||
}
|
||
},
|
||
computed: {
|
||
hasChildren() {
|
||
return this.item.children && this.item.children.length > 0;
|
||
},
|
||
icon() {
|
||
// 無論有無子節點,皆可點擊展開/收起
|
||
return this.expanded ? '▼' : '▶';
|
||
},
|
||
isSelected() {
|
||
return this.selectedType === 'region' && this.item.uuid === this.selectedId;
|
||
}
|
||
},
|
||
methods: {
|
||
toggle() {
|
||
this.expanded = !this.expanded;
|
||
},
|
||
select() {
|
||
this.$emit('select-region', this.item);
|
||
},
|
||
selectRoom(room) {
|
||
this.selectedRoomId = room.uuid;
|
||
this.$emit('select-room', room); // 可以發事件給父組件
|
||
}
|
||
},
|
||
template: `
|
||
<div>
|
||
<span class="toggle-icon" @click="toggle">{{ icon }}</span>
|
||
<span @click="select"
|
||
class="region-item-label"
|
||
:class="{ 'selected': isSelected }">
|
||
{{ item.rooms.length>0 ? (item.name + '(' + item.rooms.length + '房)'): item.name }}
|
||
</span>
|
||
|
||
<!-- 子區域列表 -->
|
||
<ul v-if="hasChildren && expanded">
|
||
<li v-for="(child, index) in item.children" :key="child.id + '-' + index">
|
||
<region-item
|
||
:item="child"
|
||
:selected-id="selectedId"
|
||
:selected-type="selectedType"
|
||
:expand-all="expandAll"
|
||
:collapse-all="collapseAll"
|
||
@select-region="$emit('select-region', $event)"
|
||
@select-room="$emit('select-room', $event)"
|
||
@clear-expand-all="$emit('clear-expand-all')"
|
||
@clear-collapse-all="$emit('clear-collapse-all')"
|
||
/>
|
||
</li>
|
||
</ul>
|
||
|
||
<!-- 客房列表:無論是否有子區域,只要展開就顯示 -->
|
||
<ul v-if="item.rooms && item.rooms.length > 0 && expanded">
|
||
<li v-for="room in item.rooms" :key="'room-' + room.uuid"
|
||
@click="selectRoom(room)"
|
||
:class="{ 'selected-room': selectedType === 'room' && selectedId === room.uuid }"
|
||
style="cursor: pointer;">
|
||
<span class="bed-label">
|
||
🛏️ {{ room.name + ' (' + room.beds.length + '床) ' + (room.gender === true ? '(男客房)' : room.gender === false ? '(女客房)' : '') }}
|
||
</span>
|
||
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
`
|
||
});
|
||
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 {
|
||
activityList: [],
|
||
availableBedCount: {
|
||
male: 0,
|
||
female: 0,
|
||
},
|
||
guadanorder: {
|
||
order_form: {
|
||
uuid: '<%= Request.QueryString["orderid"] %>' || null,
|
||
startdate: null,
|
||
enddate: null,
|
||
note: null,
|
||
orderNo: null,
|
||
bookerName: null,
|
||
bookerPhone: null,
|
||
bookerFollowerNum: null,
|
||
activityNum: null,
|
||
},
|
||
status_items: [],
|
||
},
|
||
guadanguest: {
|
||
guest: {
|
||
uuid: null, // int?
|
||
fullName: '', // string
|
||
gender: null, // int?
|
||
phone: '', // string
|
||
idNumber: '', // string
|
||
birthday: null, // Date (建議用 date picker)
|
||
email: '', // string
|
||
address: '', // string
|
||
emergencyContact: '', // string
|
||
emergencyPhone: '', // string
|
||
status: null, // int?
|
||
notes: '' // string
|
||
},
|
||
headers: [{
|
||
text: '姓名',
|
||
value: 'name'
|
||
},
|
||
{
|
||
text: '性別',
|
||
value: 'sex'
|
||
},
|
||
{
|
||
text: '掛單開始時間',
|
||
value: 'checkinat'
|
||
},
|
||
{
|
||
text: '掛單結束時間',
|
||
value: 'checkoutat'
|
||
},
|
||
{
|
||
text: '床位',
|
||
value: 'bedName'
|
||
},
|
||
{
|
||
text: '狀態',
|
||
value: 'statusName'
|
||
},
|
||
{
|
||
text: '備註',
|
||
value: 'note'
|
||
},
|
||
{
|
||
text: '',
|
||
value: 'actions'
|
||
},
|
||
],
|
||
items: [],
|
||
showCreateGuestModal: false,
|
||
xuzhu: {
|
||
showXuzhuGuestModal: false,
|
||
currentCheckoutDate: null,
|
||
newCheckoutDate: null,
|
||
guestUuid: null,
|
||
guestBedUuid: null,
|
||
}
|
||
},
|
||
checkInGuest: {
|
||
showSelectGuadanOrderGuest: false,
|
||
isEdit: false,
|
||
|
||
inGuest: {
|
||
uuid: null,
|
||
orderNo: null,
|
||
followerNum: null,
|
||
roomUuid: null,
|
||
bedUuid: null,
|
||
checkInAt: null,
|
||
checkOutAt: null,
|
||
statuscode: null,
|
||
},
|
||
status: [],
|
||
},
|
||
region_modal: {
|
||
regions: [],
|
||
currentSelectRegion: null,
|
||
currentSelectRoom: null,
|
||
currentSelectBeds: [],
|
||
currentSelectBed: null,
|
||
showSelectBedModal: false,
|
||
selectedId: null, // 被選中項目ID
|
||
selectedType: null, // 'region' 或 'room'
|
||
expandAllFlag: false, // 控制全部展開
|
||
collapseAllFlag: false, // 控制全部收起
|
||
currentSelectBedText: null,
|
||
},
|
||
selectGuestModal: {
|
||
showSelectGuestModal: false,
|
||
currentSelectedGuest: null,
|
||
fullNameText: null,
|
||
|
||
headers: [{
|
||
text: '姓名',
|
||
value: 'u_name'
|
||
},
|
||
{
|
||
text: '電話',
|
||
value: 'phone'
|
||
},
|
||
{
|
||
text: '',
|
||
value: 'actions'
|
||
},
|
||
],
|
||
items: [],
|
||
|
||
options: { //v-data-table參數
|
||
page: 1,
|
||
itemsPerPage: 10,
|
||
sortBy: [],
|
||
sortDesc: [],
|
||
multiSort: false,
|
||
},
|
||
page: 1,
|
||
pageSize: 10,
|
||
count: 0,
|
||
footer: {
|
||
showFirstLastPage: true,
|
||
disableItemsPerPage: true,
|
||
itemsPerPageAllText: '',
|
||
itemsPerPageText: '',
|
||
},
|
||
searchNameOrPhone: null,
|
||
},
|
||
automaticBedAllocation: {
|
||
showModal: false,
|
||
|
||
// 蓮友選擇彈出視窗
|
||
followerModal: {
|
||
showModal: false,
|
||
followerList: [],
|
||
selectedFollowerItems: [],
|
||
page: 1,
|
||
pageSize: 10,
|
||
totalCount: 0,
|
||
searchNameOrPhone: '',
|
||
headers: [
|
||
{ text: '姓名', value: 'u_name' },
|
||
{ text: '電話', value: 'phone' },
|
||
{ text: '操作', value: 'actions', sortable: false }
|
||
],
|
||
},
|
||
|
||
// 已選擇的待分配列表
|
||
selectedFollowers: [],
|
||
preBeds: [],
|
||
headers: [
|
||
{ text: '姓名', value: 'u_name' },
|
||
{ text: '電話', value: 'phone' },
|
||
{ text: '性別', value: 'sex' },
|
||
{ text: '預分配床位', value: 'prebed' },
|
||
{ text: '', value: 'actions' }
|
||
],
|
||
|
||
},
|
||
|
||
}
|
||
},
|
||
methods: {
|
||
getActivityList() {
|
||
axios.post(HTTP_HOST + 'api/activity/GetList?page=1&pageSize=500', { kind: 0, subject: "" })
|
||
.then((res) => {
|
||
this.activityList = res.data.list
|
||
})
|
||
},
|
||
|
||
//掛單相關方法-------------------start
|
||
validateOrderForm() {
|
||
if (!this.guadanorder.order_form.startdate) {
|
||
this.$refs.messageModal.open({
|
||
message: '請輸入必填資訊'
|
||
});
|
||
return false;
|
||
}
|
||
if (!this.guadanorder.order_form.enddate) {
|
||
this.$refs.messageModal.open({
|
||
message: '請輸入必填資訊'
|
||
});
|
||
return false;
|
||
}
|
||
if (!this.guadanorder.order_form.bookerName) {
|
||
this.$refs.messageModal.open({
|
||
message: '請輸入姓名'
|
||
});
|
||
return false;
|
||
}
|
||
if (this.guadanorder.order_form.bookerPhone && !/^\d{2,4}-?\d{3,4}-?\d{3,4}$/.test(this.guadanorder.order_form.bookerPhone)) {
|
||
this.$refs.messageModal.open({
|
||
message: '電話輸入有誤'
|
||
});
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
|
||
},
|
||
getGuadanOrderById() {
|
||
if (this.guadanorder.order_form.uuid) {
|
||
axios.get(HTTP_HOST + 'api/guadan/getorderbyid', {
|
||
params: {
|
||
orderId: this.guadanorder.order_form.uuid
|
||
}
|
||
}).then((res) => {
|
||
this.guadanorder.order_form.note = res.data.notes;
|
||
this.guadanorder.order_form.startdate = res.data.startDate;
|
||
this.guadanorder.order_form.enddate = res.data.endDate;
|
||
this.guadanorder.order_form.orderNo = res.data.guaDanOrderNo;
|
||
this.guadanorder.order_form.bookerName = res.data.bookerName;
|
||
this.guadanorder.order_form.bookerPhone = res.data.bookerPhone;
|
||
this.guadanorder.order_form.bookerFollowerNum = res.data.bookerFollowerNum;
|
||
this.guadanorder.order_form.uuid = res.data.uuid;
|
||
this.guadanorder.order_form.activityNum = res.data.activityNum;
|
||
})
|
||
}
|
||
},
|
||
getGuadanOrderGuestByOrderNo() {
|
||
if (this.guadanorder.order_form.orderNo) {
|
||
axios.get(HTTP_HOST + 'api/guadanorderguest/getbyorderno', {
|
||
params: {
|
||
orderNo: this.guadanorder.order_form.orderNo
|
||
}
|
||
}).then((res => {
|
||
this.guadanguest.items = res.data;
|
||
}))
|
||
}
|
||
},
|
||
//掛單相關方法-------------------end
|
||
},
|
||
watch: {
|
||
'guadanorder.order_form.orderNo'(newValue, oldValue) {
|
||
if (newValue) {
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
}
|
||
},
|
||
'selectGuestModal.options': {
|
||
handler() {
|
||
this.getGuadanFollowers();
|
||
},
|
||
deep: true
|
||
},
|
||
// 分頁變化時自動刷新
|
||
'automaticBedAllocation.followerModal.page': function () {
|
||
this.getMultiSelectFollowers();
|
||
},
|
||
'automaticBedAllocation.followerModal.pageSize': function () {
|
||
this.getMultiSelectFollowers();
|
||
}
|
||
},
|
||
mounted() {
|
||
if (this.guadanorder.order_form.uuid) {
|
||
this.getGuadanOrderById();
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
}
|
||
this.getActivityList();
|
||
|
||
},
|
||
computed: {
|
||
pageCount() {
|
||
return Math.ceil(this.selectGuestModal.count / this.selectGuestModal.pageSize)
|
||
},
|
||
pageCount2: function () {
|
||
var fm = this.automaticBedAllocation.followerModal;
|
||
return Math.ceil(fm.totalCount / fm.pageSize) || 1;
|
||
}
|
||
},
|
||
});
|
||
</script>
|
||
</asp:Content> |