1805 lines
83 KiB
Plaintext
1805 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();
|
|
}).catch((error) => {
|
|
this.$refs.messageModal.open({
|
|
message: (error.response?.data?.message || error.message)
|
|
});
|
|
});
|
|
|
|
},
|
|
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> |