1801 lines
83 KiB
Plaintext
1801 lines
83 KiB
Plaintext
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="create.aspx.cs" Inherits="admin_guadan_create" %>
|
||
|
||
<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">
|
||
<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 class="row mt-3">
|
||
<button v-if="guadanorder.order_form.uuid" type="button" class="btn btn-primary mx-auto" style="width: 120px;" @click="updateGuadanOrder">修改</button>
|
||
<button v-else v-on:click="createGuadanOrder" type="button" class="btn btn-primary mx-auto" style="width: 120px;">儲存</button>
|
||
</div>
|
||
</div>
|
||
|
||
</fieldset>
|
||
<fieldset class="border rounded p-4 mb-5 shadow-sm bg-white">
|
||
<!-- 表格標題緊貼表格上方,居中 -->
|
||
<div class="d-flex align-items-center mb-3">
|
||
<!-- 左側按鈕 -->
|
||
<v-btn color="primary" variant="elevated" @click="showCheckInModal" class="me-3">
|
||
<v-icon start>mdi-plus</v-icon>
|
||
增加蓮友
|
||
</v-btn>
|
||
<v-btn @click="showAutomaticBedAllocation">
|
||
自動分配床位
|
||
</v-btn>
|
||
|
||
<!-- 中間標題(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 #item.actions="{ item }">
|
||
<div>
|
||
<!-- 取消預訂 -->
|
||
<v-btn
|
||
color="red"
|
||
variant="outlined"
|
||
size="small"
|
||
class="me-2"
|
||
:disabled="item.statuscode !== '401'"
|
||
@click="confirmDeleteGuadanOrderGuest(item)"
|
||
>
|
||
取消預訂
|
||
</v-btn>
|
||
<v-btn
|
||
color="red"
|
||
variant="outlined"
|
||
size="small"
|
||
class="me-2"
|
||
:disabled="item.statuscode !== '401'"
|
||
@click="checkinGuadanGuest(item)"
|
||
>
|
||
入住
|
||
</v-btn>
|
||
<v-btn
|
||
color="red"
|
||
variant="outlined"
|
||
size="small"
|
||
class="me-2"
|
||
:disabled="item.statuscode !== '402'"
|
||
@click="showXuzhuGuestModalMethod(item)">
|
||
續住
|
||
</v-btn>
|
||
<!-- 退房 -->
|
||
<v-btn
|
||
color="primary"
|
||
variant="outlined"
|
||
size="small"
|
||
:disabled="item.statuscode !== '402'"
|
||
@click="checkoutGuadanOrderGuest(item)"
|
||
>
|
||
<v-icon start>mdi-exit-run</v-icon>
|
||
退房
|
||
</v-btn>
|
||
</div>
|
||
</template>
|
||
|
||
|
||
</v-data-table>
|
||
</fieldset>
|
||
<!-- 🟢 續住彈出視窗 -->
|
||
<div>
|
||
<v-dialog v-model="guadanguest.xuzhu.showXuzhuGuestModal" max-width="50%">
|
||
<v-card
|
||
class="pa-6 d-flex flex-column" style="min-height: 60vh; border-radius: 12px;"
|
||
style="min-height: 40vh; border-radius: 12px; box-shadow: 0 8px 20px rgba(0,0,0,0.15);"
|
||
>
|
||
<!-- 弹窗标题 -->
|
||
<v-card-title
|
||
class="text-h6 d-flex align-center justify-space-between pb-4"
|
||
style="border-bottom: 1px solid #eee;"
|
||
>
|
||
<div class="d-flex align-center">
|
||
<span class="font-weight-bold">续住</span>
|
||
</div>
|
||
<v-btn icon @click="closeXuzhuGuestModalMethod">
|
||
<v-icon>mdi-close</v-icon>
|
||
</v-btn>
|
||
</v-card-title>
|
||
|
||
<!-- 弹窗内容 -->
|
||
<v-card-text class="flex-grow-1 py-6" style="overflow-y: auto;">
|
||
<div class="mb-4">
|
||
<span class="font-weight-medium">当前退房时间:</span>
|
||
<span class="text-primary">{{ guadanguest.xuzhu.currentCheckoutDate }}</span>
|
||
</div>
|
||
<div class="d-flex align-center">
|
||
<span class="font-weight-medium mr-2">续住后退房时间:</span>
|
||
<input
|
||
type="date"
|
||
id="newCheckoutDate"
|
||
v-model="guadanguest.xuzhu.newCheckoutDate"
|
||
class="pa-2"
|
||
style="border: 1px solid #ccc; border-radius: 6px; padding: 6px 10px;"
|
||
/>
|
||
</div>
|
||
</v-card-text>
|
||
|
||
<!-- 弹窗操作按钮 -->
|
||
<v-card-actions class="justify-end pt-4" style="border-top: 1px solid #eee;">
|
||
<v-btn color="primary" class="px-6" @click="xuzhuPost">续住</v-btn>
|
||
<v-btn text class="ml-2" @click="closeXuzhuGuestModalMethod">取消</v-btn>
|
||
</v-card-actions>
|
||
</v-card>
|
||
</v-dialog>
|
||
</div>
|
||
|
||
<!-- 🟢 掛單蓮友彈出視窗 -->
|
||
<div>
|
||
<v-dialog v-model="checkInGuest.showSelectGuadanOrderGuest" max-width="80%">
|
||
<v-card class="pa-6" style="min-height: 90vh;">
|
||
<!-- 標題列 -->
|
||
<v-card-title class="text-h6 d-flex align-center justify-space-between pb-4">
|
||
<div class="d-flex align-center">
|
||
<v-icon class="me-2">mdi-account-multiple-plus</v-icon>
|
||
<span>掛單蓮友資料</span>
|
||
</div>
|
||
<v-btn icon @click="closeCheckInModal">
|
||
<v-icon>mdi-close</v-icon>
|
||
</v-btn>
|
||
</v-card-title>
|
||
|
||
<!-- 內容區 -->
|
||
<v-card-text class="py-6">
|
||
<v-container>
|
||
<v-row dense>
|
||
<!-- 掛單單號 -->
|
||
<v-col cols="12">
|
||
<v-text-field v-model="checkInGuest.inGuest.orderNo" label="掛單單號" readonly variant="underlined"></v-text-field>
|
||
</v-col>
|
||
|
||
<!-- 入住時間 -->
|
||
<v-col cols="12" sm="6">
|
||
<v-text-field
|
||
v-model="checkInGuest.inGuest.checkInAt"
|
||
label="入住時間" type="date"
|
||
variant="underlined"></v-text-field>
|
||
</v-col>
|
||
|
||
<!-- 退房時間 -->
|
||
<v-col cols="12" sm="6">
|
||
<v-text-field
|
||
v-model="checkInGuest.inGuest.checkOutAt"
|
||
label="退房時間" type="date"
|
||
variant="underlined"></v-text-field>
|
||
</v-col>
|
||
<!-- 蓮友選擇 -->
|
||
<v-col cols="12" sm="6">
|
||
<div class="d-flex align-center" style="height: 100%;">
|
||
<v-btn color="teal" variant="outlined" block @click="showSelectGuestModalMethod">
|
||
<v-icon left class="me-2">mdi-account-search</v-icon>
|
||
選擇掛單蓮友
|
||
</v-btn>
|
||
</div>
|
||
</v-col>
|
||
|
||
<v-col cols="12" sm="6">
|
||
<div class="d-flex flex-column justify-center" style="height: 100%;">
|
||
<div>
|
||
<div class="text-muted text-sm">
|
||
已選:<strong>{{ selectGuestModal?.fullNameText || '未選擇' }}</strong>
|
||
</div>
|
||
<div class="text-info text-xs mt-1">如無符合,請先新增蓮友資料</div>
|
||
</div>
|
||
</div>
|
||
</v-col>
|
||
|
||
<!-- 床位選擇 -->
|
||
<v-col cols="12" sm="6">
|
||
<div class="d-flex align-center" style="height: 100%;">
|
||
<v-btn color="indigo" variant="outlined" block @click="openSelectBedModal">
|
||
<v-icon left class="me-2">mdi-bed</v-icon>
|
||
選擇床位
|
||
</v-btn>
|
||
</div>
|
||
</v-col>
|
||
<v-col cols="12" sm="6">
|
||
<div class="d-flex flex-column justify-center" style="height: 100%;">
|
||
<div class="text-muted text-sm">
|
||
已選床位:<strong>{{ region_modal.currentSelectBedText }}</strong>
|
||
</div>
|
||
<div class="text-info text-xs mt-1">請從床位清單中選擇</div>
|
||
</div>
|
||
</v-col>
|
||
</v-row>
|
||
</v-container>
|
||
</v-card-text>
|
||
|
||
<!-- 操作按鈕 -->
|
||
<v-card-actions class="justify-end pt-6">
|
||
<v-btn color="primary" @click="saveEditGuadanOrderGuest" v-if="checkInGuest.isEdit">
|
||
<v-icon left class="me-1">mdi-content-save</v-icon>
|
||
修改
|
||
</v-btn>
|
||
|
||
<v-btn color="primary" @click="saveCheckInGuest" v-else>
|
||
<v-icon left class="me-1">mdi-content-save</v-icon>
|
||
儲存
|
||
</v-btn>
|
||
<v-btn variant="text" @click="closeCheckInModal">
|
||
<v-icon left class="me-1">mdi-close-circle-outline</v-icon>
|
||
取消
|
||
</v-btn>
|
||
</v-card-actions>
|
||
</v-card>
|
||
<!-- 子彈出視窗打開時,添加一個透明遮罩層覆蓋整個界面 -->
|
||
<v-overlay v-if="selectGuestModal.showSelectGuestModal" :scrim="false" persistent style="z-index: 202;"></v-overlay>
|
||
</v-dialog>
|
||
</div>
|
||
<!-- 選擇床位彈出視窗 -->
|
||
<div id="bedSelectionModal" v-if="region_modal.showSelectBedModal" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||
background-color: rgba(0,0,0,0.5); z-index: 10000; display: flex; justify-content: center; align-items: center;">
|
||
<div style="background: white; min-width: 70%; min-height: 80%; max-width: 80%; max-height: 80%;
|
||
display: flex; border-radius: 4px; height: 100%;">
|
||
|
||
<div style="display: flex; width: 100%; height: 100%;">
|
||
|
||
<!-- 左側 30% -->
|
||
<div style="width: 30%; border-right: 1px solid #e8e8e8; height: 100%; display: flex; flex-direction: column;">
|
||
<div style="padding: 12px 16px; border-bottom: 1px solid #e8e8e8; flex-shrink: 0;">
|
||
<h3 style="margin: 0; font-size: 16px; font-weight: normal;">區域列表</h3> <span>當前可用床位:{{availableBedCount.male + '(男)'}} {{availableBedCount.female + '(女)'}}</span>
|
||
</div>
|
||
<div style="padding: 8px 0; overflow-y: auto; flex: 1;">
|
||
<div v-for="region in region_modal.regions" :key="region.id">
|
||
<div class="tree" style="padding: 8px 16px; cursor: pointer; display: flex; align-items: center;">
|
||
<region-item :item="region" :selected-id="region_modal.selectedId" :selected-type="region_modal.selectedType" @select-region="selectRegion" @select-room="selectRoom" :expand-all="region_modal.expandAllFlag" :collapse-all="region_modal.collapseAllFlag" @clear-expand-all="region_modal.expandAllFlag = false" @clear-collapse-all="region_modal.collapseAllFlag = false" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右側 70% -->
|
||
<div style="width: 70%; display: flex; flex-direction: column; height: 100%;">
|
||
<div style="padding: 12px 16px; border-bottom: 1px solid #e8e8e8; flex-shrink: 0;">
|
||
<h3 style="margin: 0; font-size: 16px; font-weight: normal;">床位列表</h3>
|
||
<div>掛單起訖時間:{{checkInGuest.inGuest.checkInAt|timeString('YYYY-MM-DD')}} - {{checkInGuest.inGuest.checkOutAt|timeString('YYYY-MM-DD')}}</div>
|
||
</div>
|
||
|
||
<div style="flex: 1; padding: 8px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; overflow-y: auto;">
|
||
<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' // 不可用時無法點擊
|
||
}">
|
||
<div style="font-weight: 500;">{{ bed.name }}</div>
|
||
<div style="margin-top: 4px; font-size: 12px;">
|
||
{{ bed.canUsed ? '可用' : '不可用' }}
|
||
</div>
|
||
|
||
<div
|
||
v-for="schedule in bed.schedule"
|
||
:key="schedule.id"
|
||
style="margin-top: 4px; font-size: 12px; background-color: #fafafa; padding: 4px; border-radius: 4px;"
|
||
>
|
||
<div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||
🕒 {{ schedule.scheduledate|timeString("YYYY-MM-DD") }}
|
||
</div>
|
||
<div style="color: #555;">📌 {{ schedule.title }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="padding: 12px 16px; text-align: right; border-top: 1px solid #e8e8e8; flex-shrink: 0;">
|
||
<span class="me-8 shadow-lg">當前已選擇床位: {{ region_modal.currentSelectBedText }}</span>
|
||
<button type="button" @click="cancelSelectBed" style="margin-right: 8px; padding: 4px 15px; background: #fff; border: 1px solid #d9d9d9; border-radius: 2px; cursor: pointer;">
|
||
取消
|
||
</button>
|
||
<button type="button" @click="confirmSelectBed" v-if="region_modal.currentSelectBed" style="padding: 4px 15px; background: #1890ff; color: #fff; border: 1px solid #1890ff; border-radius: 2px; cursor: pointer;">
|
||
確定
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 選擇蓮友彈出視窗 -->
|
||
<div>
|
||
<v-dialog v-model="selectGuestModal.showSelectGuestModal" max-width="70%" scrollable persistent>
|
||
<v-card>
|
||
<v-card-title class="text-h6 d-flex align-center justify-space-between pb-4">
|
||
<div class="d-flex align-center">
|
||
<v-icon class="me-2">mdi-account-multiple-plus</v-icon>
|
||
<span>蓮友列表</span>
|
||
</div>
|
||
<div class="mx-4 d-flex align-center" style="flex: 1 1 50%; max-width: 50%;">
|
||
<input class="form-control me-2" style="flex: 1 1 auto; min-width: 0;" placeholder="搜尋..." v-model="selectGuestModal.searchNameOrPhone" />
|
||
<button @click="getGuadanFollowers" type="button" class="btn btn-primary" style="white-space: nowrap;">搜索</button>
|
||
</div>
|
||
|
||
|
||
<v-btn icon @click="selectGuestModal.showSelectGuestModal=false">
|
||
<v-icon>mdi-close</v-icon>
|
||
</v-btn>
|
||
</v-card-title>
|
||
<v-card-text style="max-height: 500px; overflow-y: auto;">
|
||
<v-data-table
|
||
:headers="selectGuestModal.headers"
|
||
:items="selectGuestModal.items"
|
||
:options.sync="selectGuestModal.options"
|
||
:footer-props="selectGuestModal.footer"
|
||
hide-default-footer
|
||
:server-items-length="selectGuestModal.count"
|
||
:page.sync="selectGuestModal.page"
|
||
:items-per-page.sync="selectGuestModal.pageSize">
|
||
<template v-slot:item.actions="{item}">
|
||
<button type="button" @click="selectGuadanOrderGuest(item)" class="btn btn-primary">選擇</button>
|
||
</template>
|
||
</v-data-table>
|
||
</v-card-text>
|
||
<div class="ms-10">
|
||
<v-container>
|
||
<v-row class="align-baseline" wrap>
|
||
<v-col cols="12" md="9">
|
||
<v-pagination v-model="selectGuestModal.page" :length="pageCount">
|
||
</v-pagination>
|
||
</v-col>
|
||
<v-col class="text-truncate text-right" cols="12" md="2">
|
||
共 {{ selectGuestModal.count }} 筆, 頁數:
|
||
</v-col>
|
||
<v-col cols="6" md="1">
|
||
<v-text-field v-model="selectGuestModal.page" type="number" hide-details dense min="1" :max="pageCount" @input="selectGuestModal.page = parseInt($event, 10)"></v-text-field>
|
||
</v-col>
|
||
</v-row>
|
||
</v-container>
|
||
</div>
|
||
|
||
</v-card>
|
||
</v-dialog>
|
||
</div>
|
||
<!-- 自動分配彈出視窗 -->
|
||
<div>
|
||
<v-dialog v-model="automaticBedAllocation.showModal" max-width="80%">
|
||
<v-card class="pa-6" style="height: 90vh; display: flex; flex-direction: column;">
|
||
<!-- 標題列 -->
|
||
<v-card-title class="text-h6 d-flex align-center justify-space-between pb-4" style="flex: 0 0 auto;">
|
||
<div class="d-flex align-center">
|
||
<v-icon class="me-2">mdi-account-multiple-plus</v-icon>
|
||
<span>床位自動分配</span>
|
||
</div>
|
||
<div>
|
||
<span>當前可用床位:{{availableBedCount.male + '(男)'}} {{availableBedCount.female + '(女)'}}</span>
|
||
</div>
|
||
</v-card-title>
|
||
|
||
<!-- 內容區,高度自動填滿 -->
|
||
<div style="flex: 1 1 auto; display: flex; overflow: hidden; gap: 16px;">
|
||
|
||
<!-- 左側 30% -->
|
||
<div
|
||
style="
|
||
width: 30%;
|
||
border: 1px solid #ccc;
|
||
border-radius: 8px;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background-color: #fafafa;
|
||
box-shadow: 0 2px 6px rgb(0 0 0 / 0.08);
|
||
"
|
||
>
|
||
<!-- 左側標題 -->
|
||
<div
|
||
style="
|
||
padding: 14px 20px;
|
||
font-weight: 700;
|
||
font-size: 1.1rem;
|
||
background: linear-gradient(90deg, #6a8cff, #3b5de7);
|
||
color: white;
|
||
border-top-left-radius: 8px;
|
||
border-top-right-radius: 8px;
|
||
box-shadow: 0 2px 6px rgb(58 94 255 / 0.5);
|
||
user-select: none;
|
||
"
|
||
>
|
||
自動分配條件
|
||
</div>
|
||
<!-- 左側內容滾動區 -->
|
||
<div style="padding: 16px; overflow: auto; flex: 1;">
|
||
<!-- 這裡放左側具體內容 -->
|
||
指定區域<br />
|
||
指定房間<br />
|
||
便利設施<br />
|
||
盡量分配在同一個房間<br />
|
||
<br />
|
||
<br />
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右側 70%,上下分割 -->
|
||
<div
|
||
style="
|
||
width: 70%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
"
|
||
>
|
||
<!-- 右側上半部 -->
|
||
<div
|
||
style="
|
||
flex: 1;
|
||
border: 1px solid #ccc;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-shadow: 0 2px 6px rgb(0 0 0 / 0.08);
|
||
background-color: #fff;
|
||
"
|
||
>
|
||
<!-- 右側上半部標題列 -->
|
||
<div
|
||
style="
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 14px 20px;
|
||
font-weight: 700;
|
||
font-size: 1.1rem;
|
||
background: linear-gradient(90deg, #4f86f7, #1c5bd9);
|
||
color: white;
|
||
border-top-left-radius: 8px;
|
||
border-top-right-radius: 8px;
|
||
box-shadow: 0 3px 8px rgb(31 78 250 / 0.5);
|
||
user-select: none;
|
||
"
|
||
>
|
||
<span>待分配蓮友列表</span>
|
||
<div>
|
||
<button
|
||
@click="showautomaticBedAllocationMethod"
|
||
type="button"
|
||
style="
|
||
padding: 8px 18px;
|
||
background-color: #ffffff;
|
||
color: #1c5bd9;
|
||
border: none;
|
||
border-radius: 20px;
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
cursor: pointer;
|
||
transition: background-color 0.3s ease;
|
||
box-shadow: 0 2px 6px rgba(28, 91, 217, 0.4);
|
||
user-select: none;
|
||
"
|
||
onmouseover="this.style.backgroundColor='#e6e6e6';"
|
||
onmouseout="this.style.backgroundColor='white';"
|
||
>
|
||
選擇蓮友
|
||
</button>
|
||
<button
|
||
type="button"
|
||
style="
|
||
padding: 8px 18px;
|
||
background-color: #ffffff;
|
||
color: #1c5bd9;
|
||
border: none;
|
||
border-radius: 20px;
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
cursor: pointer;
|
||
transition: background-color 0.3s ease;
|
||
box-shadow: 0 2px 6px rgba(28, 91, 217, 0.4);
|
||
user-select: none;
|
||
"
|
||
onmouseover="this.style.backgroundColor='#e6e6e6';"
|
||
onmouseout="this.style.backgroundColor='white';"
|
||
@click="preAllocation"
|
||
>按條件自動預分配</button>
|
||
</div>
|
||
|
||
</div>
|
||
<!-- 右側上半部內容滾動區 -->
|
||
<div style="padding: 16px; overflow: auto; flex: 1;">
|
||
<v-data-table
|
||
:items="automaticBedAllocation.selectedFollowers"
|
||
:headers="automaticBedAllocation.headers">
|
||
<template #item.prebed="{item}">
|
||
{{ getPreBed(item.num)?.bedUuid}}
|
||
</template>
|
||
<template #item.actions="{item}">
|
||
<button
|
||
class="btn btn-outline-danger btn-sm"
|
||
type="button"
|
||
style="min-width: 70px;"
|
||
@click="removeSelectedFollower(item.num);removePreBed(item.num)"
|
||
>
|
||
<i class="mdi mdi-delete-outline me-1"></i> 刪除
|
||
</button>
|
||
|
||
</template>
|
||
</v-data-table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 底部按鈕區 -->
|
||
<v-card-actions
|
||
style="
|
||
justify-content: flex-end;
|
||
border-top: 1px solid #eee;
|
||
padding: 16px 24px;
|
||
background-color: #f9f9f9;
|
||
flex: 0 0 auto;
|
||
"
|
||
>
|
||
<v-btn color="primary" @click="cancelAutomaticBedAllocation">
|
||
取消分配
|
||
</v-btn>
|
||
<v-btn color="primary" @click="confirmAllocation" class="ms-3" :disabled="automaticBedAllocation.preBeds.length === 0">
|
||
確認分配
|
||
</v-btn>
|
||
</v-card-actions>
|
||
</v-card>
|
||
|
||
<!-- 子彈出視窗打開時,添加一個透明遮罩層覆蓋整個界面 -->
|
||
<v-overlay v-if="automaticBedAllocation.followerModal.showModal" :scrim="false" persistent style="z-index: 202;"></v-overlay>
|
||
</v-dialog>
|
||
</div>
|
||
<!-- 自動分配選擇蓮友彈出視窗 -->
|
||
<div>
|
||
<v-dialog v-model="automaticBedAllocation.followerModal.showModal" max-width="70%" scrollable persistent>
|
||
<v-card>
|
||
<v-card-title class="text-h6 d-flex align-center justify-space-between pb-4">
|
||
<div class="d-flex align-center">
|
||
<v-icon class="me-2">mdi-account-multiple-plus</v-icon>
|
||
<span>蓮友列表</span>
|
||
</div>
|
||
<div class="mx-4 d-flex align-center" style="flex: 1 1 50%; max-width: 50%;">
|
||
<input class="form-control me-2" style="flex: 1 1 auto; min-width: 0;" placeholder="搜尋..." v-model="automaticBedAllocation.followerModal.searchNameOrPhone" />
|
||
<button @click="getMultiSelectFollowers" type="button" class="btn btn-primary" style="white-space: nowrap;">搜索</button>
|
||
</div>
|
||
|
||
|
||
<v-btn icon @click="automaticBedAllocation.followerModal.showModal=false">
|
||
<v-icon>mdi-close</v-icon>
|
||
</v-btn>
|
||
</v-card-title>
|
||
<v-card-text style="max-height: 500px; overflow-y: auto;">
|
||
<v-data-table
|
||
:headers="automaticBedAllocation.followerModal.headers"
|
||
:items="automaticBedAllocation.followerModal.followerList"
|
||
:items-per-page="automaticBedAllocation.followerModal.pageSize"
|
||
:page.sync="automaticBedAllocation.followerModal.page"
|
||
:value="automaticBedAllocation.followerModal.selectedFollowerItems"
|
||
@input="onMultiSelectChange"
|
||
show-select
|
||
item-key="num"
|
||
hide-default-footer
|
||
:item-class="getFollowerRowClass"
|
||
>
|
||
<template #item.data-table-select="{ item, isSelected, select }">
|
||
<v-simple-checkbox
|
||
:value="isSelected"
|
||
:disabled="isFollowerDisabled(item)"
|
||
@input="select($event)"
|
||
/>
|
||
</template>
|
||
|
||
<template #header.data-table-select>
|
||
<!-- 留空即可去掉全選 -->
|
||
</template>
|
||
<template #item.actions="{item}">
|
||
{{isFollowerDisabled(item) ? '已掛單' : ''}}
|
||
</template>
|
||
</v-data-table>
|
||
</v-card-text>
|
||
<div class="ms-10">
|
||
<v-container>
|
||
<v-row class="align-baseline" wrap>
|
||
<v-col cols="12" md="9">
|
||
<!-- 分頁 -->
|
||
<v-pagination
|
||
v-model="automaticBedAllocation.followerModal.page"
|
||
:length="pageCount2"
|
||
></v-pagination>
|
||
</v-col>
|
||
<v-col class="text-truncate text-right" cols="12" md="2">
|
||
共 {{ automaticBedAllocation.followerModal.totalCount }} 筆
|
||
</v-col>
|
||
</v-row>
|
||
</v-container>
|
||
</div>
|
||
|
||
</v-card>
|
||
</v-dialog>
|
||
</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">
|
||
<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: 'checkinat'
|
||
},
|
||
{
|
||
text: '掛單結束時間',
|
||
value: 'checkoutat'
|
||
},
|
||
{
|
||
text: '房間',
|
||
value: 'roomName'
|
||
},
|
||
{
|
||
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: {
|
||
//续住相關方法--------------------start
|
||
showXuzhuGuestModalMethod(guest) {
|
||
if (!guest.checkoutat) {
|
||
return;
|
||
}
|
||
this.guadanguest.xuzhu.showXuzhuGuestModal = true;
|
||
this.guadanguest.xuzhu.currentCheckoutDate = guest.checkoutat;
|
||
this.guadanguest.xuzhu.guestUuid = guest.uuid;
|
||
this.guadanguest.xuzhu.guestBedUuid = guest.bedUuid;
|
||
|
||
this.$nextTick(() => { // 确保弹窗 DOM 已渲染
|
||
const input = document.getElementById('newCheckoutDate');
|
||
if (input) {
|
||
const checkoutDate = new Date(guest.checkoutat); // 用指定日期
|
||
checkoutDate.setDate(checkoutDate.getDate() + 1); // 明天
|
||
const year = checkoutDate.getFullYear();
|
||
const month = String(checkoutDate.getMonth() + 1).padStart(2, '0');
|
||
const day = String(checkoutDate.getDate()).padStart(2, '0');
|
||
const tomorrow = `${year}-${month}-${day}`;
|
||
input.min = tomorrow; // 限制最小值
|
||
//input.value = tomorrow; // 默认选中明天
|
||
}
|
||
});
|
||
console.log(guest.checkoutat)
|
||
},
|
||
closeXuzhuGuestModalMethod() {
|
||
console.log(this.guadanguest.xuzhu.newCheckoutDate)
|
||
this.guadanguest.xuzhu.showXuzhuGuestModal = false;
|
||
this.guadanguest.xuzhu.currentCheckoutDate = null;
|
||
this.guadanguest.xuzhu.newCheckoutDate = null;
|
||
this.guadanguest.xuzhu.guestUuid = null;
|
||
this.guadanguest.xuzhu.guestBedUuid = null;
|
||
console.log(this.guadanguest.xuzhu.newCheckoutDate)
|
||
console.log(this.guadanguest.xuzhu.currentCheckoutDate)
|
||
console.log(this.guadanguest.xuzhu.guestUuid)
|
||
console.log(this.guadanguest.xuzhu.guestBedUuid)
|
||
},
|
||
xuzhuPost() {
|
||
// 校验必填
|
||
if (!this.guadanguest.xuzhu.guestUuid || !this.guadanguest.xuzhu.guestBedUuid) {
|
||
alert("GuestUuid 和 GuestBedUuid 不能为空");
|
||
return;
|
||
}
|
||
if (!this.guadanguest.xuzhu.newCheckoutDate || !this.guadanguest.xuzhu.currentCheckoutDate) {
|
||
alert("续住时间不能为空");
|
||
return;
|
||
}
|
||
|
||
const payload = {
|
||
guestUuid: this.guadanguest.xuzhu.guestUuid,
|
||
guestBedUuid: this.guadanguest.xuzhu.guestBedUuid,
|
||
currentCheckoutDate: this.guadanguest.xuzhu.currentCheckoutDate,
|
||
newCheckoutDate: this.guadanguest.xuzhu.newCheckoutDate
|
||
};
|
||
|
||
axios.post('/api/guadanorderguest/xuzhu', payload)
|
||
.then((res) => {
|
||
this.$refs.messageModal.open({
|
||
title: '续住成功',
|
||
message: '客人续住已处理',
|
||
status: 'success',
|
||
callback: () => {
|
||
// 弹窗关闭后的回调
|
||
try {
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
}
|
||
catch (error) {
|
||
console.error("发生错误:", error.message);
|
||
} finally {
|
||
this.closeXuzhuGuestModalMethod();
|
||
}
|
||
}
|
||
});
|
||
})
|
||
.catch((error) => {
|
||
this.$refs.messageModal.open({
|
||
title: '续住失败',
|
||
message: error.response?.data?.message || '系统异常,请稍后重试',
|
||
status: 'error'
|
||
});
|
||
});
|
||
},
|
||
//续住相關方法--------------------end
|
||
getActivityList() {
|
||
axios.post('/api/activity/GetList?page=1&pageSize=500', { kind: 0, subject: "" })
|
||
.then((res) => {
|
||
this.activityList = res.data.list
|
||
})
|
||
},
|
||
getavailablebedcountbytime(startTime, endTime) {
|
||
axios.get('/api/region/bed/getavailablebedcountbytime', {
|
||
params: {
|
||
startTime: startTime,
|
||
endTime: endTime
|
||
}
|
||
}).then((res) => {
|
||
this.availableBedCount = res.data;
|
||
})
|
||
},
|
||
//自動分配相關方法--------------------start
|
||
showAutomaticBedAllocation() {
|
||
if (!this.guadanorder.order_form.uuid) {
|
||
this.$refs.messageModal.open({
|
||
message: '請先創建掛單',
|
||
});
|
||
return;
|
||
}
|
||
this.automaticBedAllocation.showModal = true;
|
||
this.getavailablebedcountbytime(this.guadanorder.order_form.startdate, this.guadanorder.order_form.enddate);
|
||
},
|
||
confirmAllocation() {
|
||
//確認分配
|
||
axios.post('/api/region/bed/confirmallocation', {
|
||
preBeds: this.automaticBedAllocation.preBeds,
|
||
orderNo: this.guadanorder.order_form.orderNo,
|
||
checkInAt: this.guadanorder.order_form.startdate,
|
||
checkOutAt: this.guadanorder.order_form.enddate
|
||
})
|
||
.then(res => {
|
||
this.resetAutomaticBedAllocation();
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
});
|
||
|
||
},
|
||
preAllocation() {
|
||
const follower = this.automaticBedAllocation.selectedFollowers.map(({ num, sex }) => ({
|
||
num,
|
||
sex: sex === '男眾' ? 'M' : sex === '女眾' ? 'F' : ''
|
||
}));
|
||
console.log(follower);
|
||
const payload = {
|
||
PreBeds: follower, // 這裡傳待分配人員列表
|
||
OrderNo: this.guadanorder.order_form.orderNo || '', // 訂單號
|
||
CheckInAt: this.guadanorder.order_form.startdate || new Date(), // 入住時間
|
||
CheckOutAt: this.guadanorder.order_form.enddate || null // 退房時間,可為空
|
||
};
|
||
axios.post('/api/region/bed/preallocation', payload)
|
||
.then(res => {
|
||
this.automaticBedAllocation.preBeds = res.data.data;
|
||
})
|
||
.catch(error => {
|
||
this.$refs.messageModal.open({
|
||
title: '分配失敗',
|
||
message: (error.response?.data?.message || error.message)
|
||
});
|
||
});
|
||
},
|
||
getPreBed(num) {
|
||
return this.automaticBedAllocation.preBeds.find(a => a.num == num) || null;
|
||
},
|
||
resetAutomaticBedAllocation() {
|
||
this.automaticBedAllocation.showModal = false;
|
||
this.automaticBedAllocation.followerModal.showModal = false;
|
||
this.automaticBedAllocation.followerModal.followerList = [];
|
||
this.automaticBedAllocation.followerModal.selectedFollowerItems = [];
|
||
this.automaticBedAllocation.followerModal.page = 1;
|
||
this.automaticBedAllocation.followerModal.pageSize = 10;
|
||
this.automaticBedAllocation.followerModal.totalCount = 0;
|
||
this.automaticBedAllocation.selectedFollowers = [];
|
||
this.automaticBedAllocation.preBeds = [];
|
||
this.automaticBedAllocation.searchNameOrPhone = '';
|
||
},
|
||
cancelAutomaticBedAllocation() {
|
||
this.resetAutomaticBedAllocation();
|
||
},
|
||
isFollowerDisabled(item) {
|
||
return this.guadanguest.items.some(f => f.followerNum === item.num);
|
||
},
|
||
getFollowerRowClass(item) {
|
||
return this.isFollowerDisabled(item) ? 'bg-secondary' : '';
|
||
},
|
||
onMultiSelectChange(selectedItems) {
|
||
// 這裡拿到最新選中的列表
|
||
this.automaticBedAllocation.followerModal.selectedFollowerItems = selectedItems;
|
||
this.automaticBedAllocation.selectedFollowers = [...selectedItems];
|
||
},
|
||
showautomaticBedAllocationMethod() {
|
||
this.automaticBedAllocation.followerModal.showModal = true;
|
||
this.getMultiSelectFollowers();
|
||
},
|
||
// 獲取蓮友分頁數據
|
||
getMultiSelectFollowers: function () {
|
||
var fm = this.automaticBedAllocation.followerModal;
|
||
var self = this;
|
||
axios.post('/api/lianyou/getfollowers', null, {
|
||
params: {
|
||
page: fm.page,
|
||
pageSize: fm.pageSize,
|
||
searchName: fm.searchNameOrPhone || null
|
||
}
|
||
}).then(function (res) {
|
||
fm.followerList = res.data.data || [];
|
||
fm.totalCount = res.data.count || 0;
|
||
}).catch(function (err) {
|
||
console.error('獲取蓮友列表失敗', err);
|
||
});
|
||
},
|
||
|
||
// 選擇蓮友到右側
|
||
selectMultiFollower: function (item) {
|
||
var exists = this.automaticBedAllocation.selectedFollowers.some(function (f) {
|
||
return f.num === item.num;
|
||
});
|
||
if (!exists) {
|
||
this.automaticBedAllocation.selectedFollowers.push(item);
|
||
console.log(item)
|
||
}
|
||
},
|
||
|
||
// 移除已選蓮友
|
||
removeSelectedFollower: function (num) {
|
||
var arr = this.automaticBedAllocation.selectedFollowers;
|
||
this.automaticBedAllocation.selectedFollowers = arr.filter(function (f) {
|
||
return f.num !== num;
|
||
});
|
||
this.automaticBedAllocation.followerModal.selectedFollowerItems = this.automaticBedAllocation.followerModal.selectedFollowerItems.filter(f => f.num !== num);
|
||
|
||
},
|
||
removePreBed(num) {
|
||
this.automaticBedAllocation.preBeds = this.automaticBedAllocation.preBeds.filter(item => item.num !== num);
|
||
console.log(this.automaticBedAllocation.preBeds.length)
|
||
},
|
||
//自動分配相關方法--------------------end
|
||
//掛單相關方法-------------------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('/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('/api/guadanorderguest/getbyorderno', {
|
||
params: {
|
||
orderNo: this.guadanorder.order_form.orderNo
|
||
}
|
||
}).then((res => {
|
||
this.guadanguest.items = res.data;
|
||
}))
|
||
}
|
||
},
|
||
getGuadanOrderStatus() {
|
||
axios.get('/api/region/guadan/status/list')
|
||
.then((res) => {
|
||
this.guadanorder.status_items = res.data;
|
||
})
|
||
},
|
||
createGuadanOrder() {
|
||
if (!this.validateOrderForm()) {
|
||
return;
|
||
}
|
||
axios.post('/api/guadan/create', this.guadanorder.order_form)
|
||
.then((res => {
|
||
this.$refs.messageModal.open({
|
||
title: '掛單提示',
|
||
message: '掛單資料建立成功'
|
||
})
|
||
this.guadanorder.order_form.uuid = res.data.uuid;
|
||
this.guadanorder.order_form.orderNo = res.data.guaDanOrderNo;
|
||
console.log(this.guadanorder.order_form.uuid);
|
||
|
||
// 跳转到当前 URL 加上 ?orderId=UUID
|
||
const currentUrl = window.location.origin + window.location.pathname;
|
||
window.location.href = `${currentUrl}?orderId=${res.data.guaDanOrderNo}`;
|
||
})).catch((error) => {
|
||
this.$refs.messageModal.open({
|
||
title: '錯誤',
|
||
message: '建立掛單資料失敗:' + (error.response?.data?.message || error.message)
|
||
});
|
||
});
|
||
},
|
||
updateGuadanOrder() {
|
||
if (!this.validateOrderForm()) {
|
||
return;
|
||
}
|
||
axios.post('/api/guadan/update', this.guadanorder.order_form)
|
||
.then((res => {
|
||
this.$refs.messageModal.open({
|
||
title: '掛單提示',
|
||
message: '掛單資料更新成功'
|
||
})
|
||
this.guadanorder.order_form.uuid = res.data.uuid;
|
||
console.log(this.guadanorder.order_form.uuid);
|
||
})).catch((error) => {
|
||
this.$refs.messageModal.open({
|
||
title: '錯誤',
|
||
message: '建立掛單資料失敗:' + (error.response?.data?.message || error.message)
|
||
});
|
||
});
|
||
},
|
||
showCheckInModal() {
|
||
if (!this.guadanorder.order_form.uuid) {
|
||
this.$refs.messageModal.open({
|
||
message: '請先創建掛單',
|
||
});
|
||
return;
|
||
}
|
||
this.checkInGuest.inGuest.checkInAt = this.guadanorder.order_form.startdate || null;
|
||
this.checkInGuest.inGuest.checkOutAt = this.guadanorder.order_form.enddate || null;
|
||
this.checkInGuest.inGuest.orderNo = this.guadanorder.order_form.orderNo;
|
||
this.checkInGuest.showSelectGuadanOrderGuest = true;
|
||
},
|
||
closeCheckInModal() {
|
||
this.resetRegionModal();
|
||
this.resetInGuest();
|
||
this.selectGuestModal.currentSelectedGuest = null;
|
||
this.selectGuestModal.fullNameText = null;
|
||
this.checkInGuest.showSelectGuadanOrderGuest = false;
|
||
this.checkInGuest.isEdit = false;
|
||
},
|
||
//掛單相關方法-------------------end
|
||
|
||
//增加蓮友方法-------------------start
|
||
checkoutGuadanOrderGuest(guest) {
|
||
this.$refs.confirmModal.open({
|
||
message: `確定要將 ${guest.follower.u_name || ''} 退房嗎?`,
|
||
onConfirm: async () => {
|
||
try {
|
||
const response = await axios.post(`/api/guadanorderguest/checkout`, null, {
|
||
params: { uuid: guest.uuid }
|
||
});
|
||
|
||
// 成功提示
|
||
this.$refs.messageModal.open({
|
||
title: '操作成功',
|
||
message: '退房成功!',
|
||
status: 'success'
|
||
});
|
||
|
||
// 更新狀態並刷新資料
|
||
guest.statusCode = "403"; // 已退房
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
} catch (error) {
|
||
console.error(error);
|
||
|
||
// 失敗提示
|
||
this.$refs.messageModal.open({
|
||
title: '操作失敗',
|
||
message: error.response?.data?.message || '退房過程中發生錯誤!',
|
||
status: 'error'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
|
||
resetInGuest() {
|
||
this.checkInGuest.inGuest = {
|
||
uuid: null,
|
||
orderNo: null,
|
||
guestId: null,
|
||
roomUuid: null,
|
||
bedUuid: null,
|
||
checkInAt: null,
|
||
checkOutAt: null,
|
||
statuscode: null,
|
||
};
|
||
},
|
||
setInGuest() {
|
||
if (this.region_modal.currentSelectRoom) {
|
||
this.checkInGuest.inGuest.roomUuid = this.region_modal.currentSelectRoom.uuid;
|
||
}
|
||
if (this.region_modal.currentSelectBed) {
|
||
this.checkInGuest.inGuest.bedUuid = this.region_modal.currentSelectBed.uuid;
|
||
}
|
||
console.log(this.checkInGuest.inGuest)
|
||
},
|
||
validateCheckInGuest() {
|
||
//驗證添加掛單蓮友的時候輸入資料
|
||
if (!this.checkInGuest.inGuest.orderNo) {
|
||
this.$refs.messageModal.open({
|
||
message: '掛單編號不能為空'
|
||
})
|
||
}
|
||
if (!this.checkInGuest.inGuest.checkInAt) {
|
||
this.$refs.messageModal.open({
|
||
message: '入住時間不能為空'
|
||
})
|
||
return false;
|
||
}
|
||
if (!this.checkInGuest.inGuest.checkOutAt) {
|
||
this.$refs.messageModal.open({
|
||
message: '退單時間不能為空'
|
||
})
|
||
return false;
|
||
}
|
||
|
||
if (!this.checkInGuest.inGuest.roomUuid) {
|
||
this.$refs.messageModal.open({
|
||
message: '請選擇房間'
|
||
})
|
||
return false;
|
||
}
|
||
if (!this.checkInGuest.inGuest.bedUuid) {
|
||
this.$refs.messageModal.open({
|
||
message: '請選擇房間'
|
||
})
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
},
|
||
async saveCheckInGuest() {
|
||
if (!this.validateCheckInGuest()) {
|
||
return;
|
||
}
|
||
try {
|
||
const res = await this.createCheckInGuest();
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
this.closeCheckInModal();
|
||
} catch (error) {
|
||
console.log(error)
|
||
this.$refs.messageModal.open({
|
||
message: (error.response?.data?.message || error.message)
|
||
})
|
||
}
|
||
|
||
},
|
||
createCheckInGuest() {
|
||
return axios.post('/api/guadanorderguest/create', this.checkInGuest.inGuest)
|
||
},
|
||
checkBedAndFollower() {
|
||
this.checkInGuest.inGuest
|
||
},
|
||
getGuadanGuestStatus() {
|
||
axios.get('/api/region/bed/status/list')
|
||
.then((res) => {
|
||
this.checkInGuest.status = res.data.filter(item => item.category === 4 && item.code != '404');
|
||
})
|
||
},
|
||
//增加蓮友方法-------------------end
|
||
|
||
//蓮友選擇相關方法---------------start
|
||
getGuadanFollowers() {
|
||
//從信眾表中獲取掛單人列表
|
||
const {
|
||
sortBy,
|
||
sortDesc,
|
||
page,
|
||
itemsPerPage
|
||
} = this.selectGuestModal.options
|
||
const params = {
|
||
sortBy: sortBy[0],
|
||
sortDesc: sortDesc[0],
|
||
page: page,
|
||
pageSize: itemsPerPage,
|
||
searchName: this.selectGuestModal.searchNameOrPhone
|
||
};
|
||
axios.post('/api/lianyou/getfollowers', null, {
|
||
params: params
|
||
}).then((res) => {
|
||
this.selectGuestModal.items = res.data.data
|
||
this.selectGuestModal.count = res.data.count;
|
||
})
|
||
},
|
||
showSelectGuestModalMethod() {
|
||
this.selectGuestModal.showSelectGuestModal = true;
|
||
},
|
||
selectGuadanOrderGuest(guest) {
|
||
this.selectGuestModal.currentSelectedGuest = guest;
|
||
console.log('----------' + guest)
|
||
this.selectGuestModal.fullNameText = guest.u_name;
|
||
this.checkInGuest.inGuest.followerNum = guest.num;
|
||
this.selectGuestModal.showSelectGuestModal = false;
|
||
},
|
||
editGuadanOrderGuest(guest) {
|
||
this.checkInGuest.inGuest.uuid = guest.uuid;
|
||
this.checkInGuest.inGuest.followerNum = guest.followerNum;
|
||
this.checkInGuest.inGuest.roomUuid = guest.roomUuid;
|
||
this.checkInGuest.inGuest.bedUuid = guest.bedUuid;
|
||
this.checkInGuest.inGuest.orderNo = guest.orderNo;
|
||
this.checkInGuest.inGuest.checkInAt = guest.checkinat;
|
||
this.checkInGuest.inGuest.checkOutAt = guest.checkoutat;
|
||
this.checkInGuest.showSelectGuadanOrderGuest = true;
|
||
this.checkInGuest.isEdit = true;
|
||
this.getCurrentSelectBedTextByBedId(guest.bedUuid);
|
||
this.selectGuestModal.fullNameText = guest.follower?.u_name;
|
||
this.selectGuestModal.currentSelectedGuest = guest.follower;
|
||
this.checkInGuest.inGuest.statuscode = guest.statuscode;
|
||
|
||
},
|
||
async saveEditGuadanOrderGuest() {
|
||
try {
|
||
const res = await axios.post('/api/guadanorderguest/update', this.checkInGuest.inGuest)
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
this.closeCheckInModal();
|
||
} catch (error) {
|
||
this.$refs.messageModal.open({
|
||
message: (error.response?.data?.message || error.message)
|
||
})
|
||
}
|
||
|
||
},
|
||
deleteGuadanOrderGuest(guest) {
|
||
axios.post('/api/guadanorderguest/cancel?uuid=' + guest.uuid)
|
||
.then((res) => {
|
||
this.guadanguest.items = this.guadanguest.items.filter(i => i.uuid != guest.uuid);
|
||
}).catch((error) => {
|
||
this.$refs.messageModal.open({
|
||
message: (error.response?.data?.message || error.message)
|
||
})
|
||
});
|
||
},
|
||
confirmDeleteGuadanOrderGuest(guest) {
|
||
this.$refs.confirmModal.open({
|
||
'message': '確認取消?',
|
||
'onConfirm': () => {
|
||
this.deleteGuadanOrderGuest(guest);
|
||
}
|
||
})
|
||
},
|
||
async checkinGuadanGuest(guest) {
|
||
// 先確認操作
|
||
this.$refs.confirmModal.open({
|
||
message: '確認入住?',
|
||
onConfirm: async () => {
|
||
try {
|
||
// 發送請求到後端 API
|
||
const response = await axios.post(`/api/guadanorderguest/checkin`, null, {
|
||
params: { uuid: guest.uuid }
|
||
});
|
||
|
||
// 成功提示
|
||
this.$refs.messageModal.open({
|
||
title: '操作成功',
|
||
message: '入住成功!',
|
||
status: 'success'
|
||
});
|
||
|
||
// 更新本地列表,修改狀態為已入住 (402)
|
||
guest.statusCode = "402";
|
||
|
||
// 如果需要刷新整個列表,也可以調用
|
||
this.getGuadanOrderGuestByOrderNo();
|
||
|
||
} catch (error) {
|
||
console.error(error);
|
||
|
||
// 失敗提示
|
||
this.$refs.messageModal.open({
|
||
title: '操作失敗',
|
||
message: error.response?.data?.message || '入住過程中發生錯誤!',
|
||
status: 'error'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
//蓮友選擇相關方法---------------end
|
||
|
||
//床位選擇相關方法----------------start
|
||
async loadRegions() {
|
||
const res = await axios.post('/api/region/getRegionList');
|
||
this.region_modal.regions = res.data;
|
||
},
|
||
async loadRegionsByGender() {
|
||
let isMale = null;
|
||
const guest = this.selectGuestModal.currentSelectedGuest;
|
||
|
||
if (guest && guest.sex) {
|
||
if (guest.sex === '男眾') {
|
||
isMale = true;
|
||
} else if (guest.sex === '女眾') {
|
||
isMale = false;
|
||
}
|
||
}
|
||
|
||
const res = await axios.post('/api/region/getRegionListByGender', {
|
||
IsMale: isMale
|
||
});
|
||
|
||
this.region_modal.regions = res.data;
|
||
},
|
||
selectRegion(region) {
|
||
this.region_modal.currentSelectRegion = region;
|
||
this.region_modal.selectedId = region.uuid;
|
||
this.region_modal.selectedType = 'region';
|
||
},
|
||
selectRoom(room) {
|
||
this.region_modal.currentSelectRoom = room;
|
||
this.region_modal.selectedId = room.uuid;
|
||
this.region_modal.selectedType = 'room';
|
||
this.region_modal.currentSelectBeds = room.beds;
|
||
if (this.checkInGuest.inGuest.checkInAt && this.checkInGuest.inGuest.checkOutAt) {
|
||
axios.get('/api/region/room/bed/list', {
|
||
params: {
|
||
roomUuid: room.uuid,
|
||
StartTime: this.checkInGuest.inGuest.checkInAt,
|
||
EndTime: this.checkInGuest.inGuest.checkOutAt
|
||
}
|
||
})
|
||
.then((res) => {
|
||
this.region_modal.currentSelectBeds = res.data
|
||
})
|
||
}
|
||
},
|
||
selectBed(bed) {
|
||
this.region_modal.currentSelectBed = bed;
|
||
this.getCurrentSelectBedTextByBedId(bed.uuid);
|
||
},
|
||
GetRegionRoomBedListByRoomId(roomUuid) {
|
||
if (this.checkInGuest.inGuest.checkInAt && this.checkInGuest.inGuest.checkOutAt) {
|
||
axios.get('/api/region/bed/list')
|
||
.then((res) => {
|
||
|
||
})
|
||
}
|
||
},
|
||
cancelSelectBed() {
|
||
this.resetRegionModal();
|
||
this.getCurrentSelectBedTextByBedId(this.checkInGuest.inGuest.bedUuid);
|
||
this.region_modal.showSelectBedModal = false;
|
||
},
|
||
confirmSelectBed() {
|
||
this.region_modal.showSelectBedModal = false;
|
||
//this.setOrderForm();
|
||
this.setInGuest();
|
||
},
|
||
openSelectBedModal() {
|
||
if (!this.checkInGuest.inGuest.checkInAt || !this.checkInGuest.inGuest.checkOutAt) {
|
||
this.$refs.messageModal.open({
|
||
message: '請先選擇時間'
|
||
});
|
||
return;
|
||
}
|
||
this.region_modal.showSelectBedModal = true;
|
||
this.loadRegionsByGender();
|
||
this.getavailablebedcountbytime(this.checkInGuest.inGuest.checkInAt, this.checkInGuest.inGuest.checkOutAt);
|
||
},
|
||
getCurrentSelectBedTextByBedId(bedUuid) {
|
||
//獲取當前選擇的床位和所有上級組成的字串
|
||
this.region_modal.currentSelectBedText = this.findBedPath(this.region_modal.regions, bedUuid)
|
||
},
|
||
findBedPath(data, targetBedId) {
|
||
const path = [];
|
||
|
||
function dfs(regions, currentPath) {
|
||
for (const region of regions) {
|
||
const regionPath = [...currentPath, region.name];
|
||
// 遍歷當前區域的 rooms
|
||
for (const room of region.rooms || []) {
|
||
const roomPath = [...regionPath, room.name];
|
||
|
||
// 尋找床位
|
||
for (const bed of room.beds || []) {
|
||
if (bed.uuid === targetBedId) {
|
||
// 找到目標床位
|
||
path.push(...roomPath, bed.name);
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
// 遍歷子區域
|
||
if (region.children && region.children.length > 0) {
|
||
if (dfs(region.children, regionPath)) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
dfs(data, []);
|
||
return path.length > 0 ? path.join(' / ') : null;
|
||
},
|
||
resetRegionModal() {
|
||
this.region_modal.currentSelectBeds = [];
|
||
this.region_modal.currentSelectRegion = null;
|
||
this.region_modal.currentSelectRoom = null;
|
||
this.region_modal.currentSelectBed = null;
|
||
this.region_modal.selectedId = null;
|
||
this.region_modal.selectedType = null;
|
||
this.region_modal.expandAllFlag = null;
|
||
this.region_modal.collapseAllFlag = null;
|
||
this.region_modal.currentSelectBedText = '';
|
||
},
|
||
//床位選擇相關方法----------------end
|
||
formatDate(datetimeStr) {
|
||
const d = new Date(datetimeStr);
|
||
return d.toLocaleDateString('zh-CN', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
});
|
||
},
|
||
|
||
},
|
||
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.loadRegions();
|
||
this.getGuadanOrderStatus();
|
||
this.getGuadanGuestStatus();
|
||
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> |