update from old git
This commit is contained in:
BIN
data/SQL/guadan_script.sql
Normal file
BIN
data/SQL/guadan_script.sql
Normal file
Binary file not shown.
21
web/App_Code/Model/Partial/GuaDanOrder.cs
Normal file
21
web/App_Code/Model/Partial/GuaDanOrder.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
/// <summary>
|
||||
/// GuaDanOrder 的摘要描述
|
||||
/// </summary>
|
||||
namespace Model
|
||||
{
|
||||
public partial class GuaDanOrder
|
||||
{
|
||||
public static string GenerateStatusCode()
|
||||
{
|
||||
string prefix = "GD";
|
||||
string datePart = DateTime.Now.ToString("yyyyMMdd");
|
||||
string randomPart = Guid.NewGuid().ToString("N").Substring(0, 4).ToUpper();
|
||||
return $"{prefix}{datePart}-{randomPart}";
|
||||
}
|
||||
}
|
||||
}
|
||||
49
web/App_Code/Model/Partial/Region.cs
Normal file
49
web/App_Code/Model/Partial/Region.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Region 的摘要描述
|
||||
/// </summary>
|
||||
namespace Model
|
||||
{
|
||||
public partial class Region
|
||||
{
|
||||
public bool IsAvailable()
|
||||
{
|
||||
//判断当前区域是否可用,需要判断父区域是否可用,如果父区域不可用,则所有字区域都不可用
|
||||
if (!this.IsActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Region region = this.Region2;
|
||||
while (region != null) {
|
||||
if (!region.IsActive) {
|
||||
return false;
|
||||
}
|
||||
region = region.Region2;
|
||||
}
|
||||
return true;
|
||||
//这里增加根据区域排程判定在指定时间段内区域是否可用
|
||||
}
|
||||
|
||||
public bool? IsMaleOrFemale()
|
||||
{
|
||||
if (this.Gender.HasValue)
|
||||
return this.Gender.Value;
|
||||
Region currentRegion = this.Region2;
|
||||
while (currentRegion != null)
|
||||
{
|
||||
if (currentRegion.Gender.HasValue)
|
||||
return currentRegion.Gender.Value;
|
||||
|
||||
currentRegion = currentRegion.Region2; // 上级区域
|
||||
}
|
||||
|
||||
// 都没有定义性别
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
144
web/App_Code/Model/Partial/RegionAndRoomAndBedSchedule.cs
Normal file
144
web/App_Code/Model/Partial/RegionAndRoomAndBedSchedule.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
/// <summary>
|
||||
/// RegionAndRoomAndBedSchedule 的摘要描述
|
||||
/// </summary>
|
||||
namespace Model
|
||||
{
|
||||
public partial class RegionAndRoomAndBedSchedule
|
||||
{
|
||||
public enum SchedulePurpose
|
||||
{
|
||||
None = -1, // 無特定用途
|
||||
|
||||
// 區域相關
|
||||
Region_Repair = 10, // 區域修繕
|
||||
Region_Cleaning = 11, // 區域清潔
|
||||
// 客房相關
|
||||
Room_Reservation = 20, // 客房預訂
|
||||
Room_Cleaning = 21, // 客房清潔
|
||||
Room_Disinfection = 22, // 客房消毒
|
||||
Room_Repair = 23, // 客房維修
|
||||
|
||||
// 床位相關
|
||||
Bed_Reservation = 30, // 床位預訂
|
||||
Bed_Cleaning = 31, // 床位清潔
|
||||
Bed_Repair = 32, // 床位維修
|
||||
Bed_Disinfection = 33, // 床位消毒
|
||||
|
||||
// 其他用途
|
||||
Event_Use = 90, // 活動使用
|
||||
Other = 99 // 其他
|
||||
}
|
||||
|
||||
public enum StatusEnum
|
||||
{
|
||||
None = 0,
|
||||
Normal = 1,
|
||||
Cancel = 2,
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找指定时间段内可用床位(无排程占用)
|
||||
/// </summary>
|
||||
/// <param name="db">数据库上下文</param>
|
||||
/// <param name="start">查询开始时间</param>
|
||||
/// <param name="end">查询结束时间</param>
|
||||
/// <returns>可用床位列表</returns>
|
||||
public static async Task<List<RegionRoomBed>> GetAvailableBedsAsync(ezEntities db, DateTime start, DateTime? end)
|
||||
{
|
||||
start = start.Date;
|
||||
end = end?.Date;
|
||||
|
||||
// 找出在日期範圍內被占用的床位 Uuid(包括長期占用 ScheduleDate = null)
|
||||
var busyBedUuidsQuery = db.RegionAndRoomAndBedSchedule
|
||||
.Where(s => s.IsDeleted == false && s.IsActive
|
||||
&& (s.ScheduleDate == null
|
||||
|| (end.HasValue
|
||||
&& s.ScheduleDate >= start
|
||||
&& s.ScheduleDate <= end.Value)));
|
||||
|
||||
var busyBedUuids = await busyBedUuidsQuery
|
||||
.Select(s => s.TargetUuid)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
// 空閒床位 = 所有床位 - 忙碌床位
|
||||
var availableBeds = await db.RegionRoomBed
|
||||
.Where(b => !busyBedUuids.Contains(b.Uuid))
|
||||
.ToListAsync();
|
||||
|
||||
return availableBeds;
|
||||
}
|
||||
public static async Task<bool> IsBedAvailableAsync(ezEntities db, Guid targetUuid, DateTime start, DateTime? end)
|
||||
{
|
||||
// 如果 end 為 null,表示長期占用,直接判斷是否已有長期占用
|
||||
if (end == null)
|
||||
{
|
||||
var hasLongTerm = await db.RegionAndRoomAndBedSchedule
|
||||
.AnyAsync(s => s.IsDeleted == false
|
||||
&& s.IsActive
|
||||
&& s.TargetUuid == targetUuid
|
||||
&& s.ScheduleDate == null);
|
||||
return !hasLongTerm;
|
||||
}
|
||||
|
||||
// 短期占用,查詢每日排程中有無衝突
|
||||
var totalDays = (end.Value.Date - start.Date).Days + 1;
|
||||
for (int i = 0; i < totalDays; i++)
|
||||
{
|
||||
var date = start.Date.AddDays(i);
|
||||
|
||||
var conflict = await db.RegionAndRoomAndBedSchedule
|
||||
.AnyAsync(s => s.IsDeleted == false
|
||||
&& s.IsActive
|
||||
&& s.TargetUuid == targetUuid
|
||||
&& s.ScheduleDate == date);
|
||||
|
||||
if (conflict)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public static async Task<object> GetAvailableBedCountsAsync(ezEntities db, DateTime start, DateTime end)
|
||||
{
|
||||
start = start.Date;
|
||||
end = end.Date;
|
||||
|
||||
// 找出所有在日期範圍內被占用的床位
|
||||
var busyBedUuids = await db.RegionAndRoomAndBedSchedule
|
||||
.Where(s => s.IsDeleted == false && s.IsActive
|
||||
&& (s.ScheduleDate == null // 長期占用
|
||||
|| (s.ScheduleDate >= start && s.ScheduleDate <= end)))
|
||||
.Select(s => s.TargetUuid)
|
||||
.Distinct()
|
||||
.ToListAsync();
|
||||
|
||||
// 可用床位 = 所有床位 - 忙碌床位
|
||||
var availableBeds = db.RegionRoomBed
|
||||
.Where(b => !busyBedUuids.Contains(b.Uuid));
|
||||
|
||||
var result = await availableBeds
|
||||
.GroupBy(b => b.Gender)
|
||||
.Select(g => new
|
||||
{
|
||||
Gender = g.Key,
|
||||
Count = g.Count()
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var male = result.Where(r => r.Gender == true).Select(r => r.Count).FirstOrDefault();
|
||||
var female = result.Where(r => r.Gender == false).Select(r => r.Count).FirstOrDefault();
|
||||
|
||||
return new { male, female };
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
50
web/App_Code/Model/Partial/RegionRoomBed.cs
Normal file
50
web/App_Code/Model/Partial/RegionRoomBed.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
/// <summary>
|
||||
/// RegionRoomBed 的摘要描述
|
||||
/// </summary>
|
||||
namespace Model
|
||||
{
|
||||
public partial class RegionRoomBed
|
||||
{
|
||||
public bool IsAvailable()
|
||||
{
|
||||
//判断床位是否可用:自身是否启用
|
||||
//床位使用排程是否可用
|
||||
//是否需要传入时间?指定时间间隔内
|
||||
if (!this.IsActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return this.Room.IsAvailable();
|
||||
//根据床位排程判定床位是否可用
|
||||
}
|
||||
public bool IsAvailableDuring(DateTime checkInAt, DateTime? checkOutAt, ezEntities _db)
|
||||
{
|
||||
// 床位本身不可用,直接返回 false
|
||||
if (!this.IsActive || !this.Room.IsAvailable())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果資料庫 ScheduleDate 是 date 型別,本身沒有時間部分,可以直接比較
|
||||
var conflict = _db.RegionAndRoomAndBedSchedule.Any(s =>
|
||||
s.TargetUuid == this.Uuid &&
|
||||
s.IsActive &&
|
||||
!s.IsDeleted &&
|
||||
(
|
||||
s.ScheduleDate == null || // 長期占用
|
||||
(checkOutAt.HasValue
|
||||
? (s.ScheduleDate >= checkInAt && s.ScheduleDate <= checkOutAt.Value)
|
||||
: s.ScheduleDate >= checkInAt)
|
||||
)
|
||||
);
|
||||
|
||||
return !conflict;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
web/App_Code/Model/Partial/RegionRoomBedStatus.cs
Normal file
55
web/App_Code/Model/Partial/RegionRoomBedStatus.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using Model;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Model
|
||||
{
|
||||
[MetadataType(typeof(RegionRoomBedStatusMetadata))]
|
||||
public partial class RegionRoomBedStatus
|
||||
{
|
||||
private class RegionRoomBedStatusMetadata
|
||||
{
|
||||
//[JsonIgnore]
|
||||
//public virtual ICollection<RegionRoomBed> RegionRoomBed { get; set; }
|
||||
|
||||
//[JsonIgnore]
|
||||
//public virtual ICollection<GuaDanOrder> GuaDanOrder { get; set; }
|
||||
}
|
||||
|
||||
public enum CategoryEnum
|
||||
{
|
||||
Unknown = 0,
|
||||
BedStatus = 1,
|
||||
GuaDanStatus = 2,
|
||||
RoomStatus = 3,
|
||||
GuadanLianyouStatus = 4,
|
||||
}
|
||||
[JsonProperty("CategoryName")]
|
||||
public string CategoryName => GetCategoryName(this.Category);
|
||||
public static string GetCategoryName(int? category)
|
||||
{
|
||||
if (category == null) return "";
|
||||
switch ((CategoryEnum)category)
|
||||
{
|
||||
case CategoryEnum.BedStatus: return "床位狀態";
|
||||
case CategoryEnum.GuaDanStatus: return "掛單狀態";
|
||||
case CategoryEnum.RoomStatus: return "房間狀態";
|
||||
case CategoryEnum.GuadanLianyouStatus: return "個人掛單狀態";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable GetCategoryList()
|
||||
{
|
||||
var list = new ArrayList();
|
||||
list.Add(new { Value = (int)CategoryEnum.BedStatus, Text = "床位狀態" });
|
||||
list.Add(new { Value = (int)CategoryEnum.GuaDanStatus, Text = "掛單狀態" });
|
||||
list.Add(new { Value = (int)CategoryEnum.RoomStatus, Text = "房間狀態" });
|
||||
list.Add(new { Value = (int)CategoryEnum.GuadanLianyouStatus, Text = "個人掛單狀態" });
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
web/App_Code/Model/Partial/Room.cs
Normal file
23
web/App_Code/Model/Partial/Room.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Model
|
||||
{
|
||||
public partial class Room
|
||||
{
|
||||
public bool IsAvailable()
|
||||
{
|
||||
//判断当前房间是否可用,需要判断父区域是否可用,如果父区域不可用,则所有子区域都不可用
|
||||
// 有值且是 false,表示不可用
|
||||
if (this.IsActive.HasValue && this.IsActive.Value == false)
|
||||
return false;
|
||||
return this.Region.IsAvailable();
|
||||
//这里增加根据房间排程判定在指定时间段内房间是否可用
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Room 的摘要描述
|
||||
/// </summary>
|
||||
32
web/App_Code/Model/ViewModel/GuaDanGuestView.cs
Normal file
32
web/App_Code/Model/ViewModel/GuaDanGuestView.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
/// <summary>
|
||||
/// GuaDanGuestView 的摘要描述
|
||||
/// </summary>
|
||||
public class GuaDanGuestView
|
||||
{
|
||||
public GuaDanGuestView()
|
||||
{
|
||||
//
|
||||
// TODO: 在這裡新增建構函式邏輯
|
||||
//
|
||||
}
|
||||
public int? Id { get; set; }
|
||||
[Required]
|
||||
[StringLength(100, MinimumLength = 1)]
|
||||
public string FullName { get; set; }
|
||||
public Nullable<int> Gender { get; set; }
|
||||
public string Phone { get; set; }
|
||||
public string IDNumber { get; set; }
|
||||
public Nullable<System.DateTime> Birthday { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Address { get; set; }
|
||||
public string EmergencyContact { get; set; }
|
||||
public string EmergencyPhone { get; set; }
|
||||
public Nullable<int> Status { get; set; }
|
||||
public string Notes { get; set; }
|
||||
}
|
||||
23
web/App_Code/Model/ViewModel/GuaDanOrderView.cs
Normal file
23
web/App_Code/Model/ViewModel/GuaDanOrderView.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
/// <summary>
|
||||
/// GuaDanOrderView 的摘要描述
|
||||
/// </summary>
|
||||
public class GuaDanOrderView
|
||||
{
|
||||
public Guid? Uuid { get; set; } = null;
|
||||
public System.DateTime startdate { get; set; }
|
||||
public Nullable<System.DateTime> enddate { get; set; }
|
||||
public Guid? statusUuid { get; set; } = null;
|
||||
public Nullable<int> createuser { get; set; }
|
||||
public System.DateTime createdat { get; set; }
|
||||
public System.DateTime updatedat { get; set; }
|
||||
public string note { get; set; }
|
||||
public string bookerName { get; set; }
|
||||
public string bookerPhone { get; set; }
|
||||
public int? bookerFollowerNum { get; set; }
|
||||
|
||||
}
|
||||
214
web/App_Code/api/guadanOrderController.cs
Normal file
214
web/App_Code/api/guadanOrderController.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using static regionController;
|
||||
|
||||
/// <summary>
|
||||
/// guadanOderController 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class guadanOrderController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
[HttpGet]
|
||||
[Route("api/guadan/list")]
|
||||
public async Task<IHttpActionResult> getGuadanList()
|
||||
{
|
||||
var data = await _db.GuaDanOrder.OrderByDescending(b => b.CreatedAt)
|
||||
.Select(a => new
|
||||
{
|
||||
uuid = a.Uuid,
|
||||
guaDanOrderNo = a.GuaDanOrderNo,
|
||||
start_date = a.StartDate,
|
||||
end_date = a.EndDate,
|
||||
created_at = a.CreatedAt,
|
||||
updated_at = a.UpdatedAt,
|
||||
notes = a.Notes,
|
||||
bookerName = a.BookerName,
|
||||
guest_count = _db.GuaDanOrderGuest.Where(c => c.GuaDanOrderNo == a.GuaDanOrderNo).Count(),
|
||||
}).ToListAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("api/guadan/getorderbyid")]
|
||||
public async Task<IHttpActionResult> getGuadanOrderById(string orderId)
|
||||
{
|
||||
var order = await _db.GuaDanOrder.Where(a => a.GuaDanOrderNo == orderId).FirstOrDefaultAsync();
|
||||
if (order == null)
|
||||
{
|
||||
return BadRequest("未找到对应订单");
|
||||
}
|
||||
var result = new
|
||||
{
|
||||
order.admin,
|
||||
order.followers,
|
||||
StartDate = order.StartDate?.ToString("yyyy-MM-dd"),
|
||||
EndDate = order.EndDate?.ToString("yyyy-MM-dd"),
|
||||
order.CreateUser,
|
||||
order.CreatedAt,
|
||||
order.UpdatedAt,
|
||||
order.Notes,
|
||||
order.GuaDanOrderNo,
|
||||
order.BookerFollowerNum,
|
||||
order.BookerName,
|
||||
order.BookerPhone,
|
||||
order.IsDeleted,
|
||||
order.Uuid,
|
||||
};
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/guadan/create")]
|
||||
public async Task<IHttpActionResult> createGuadanOrder(GuaDanOrderView model)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return BadRequest("掛單資料不可為空");
|
||||
}
|
||||
if(model.Uuid.HasValue)
|
||||
{
|
||||
return BadRequest("已存在对应挂单资料");
|
||||
}
|
||||
try
|
||||
{
|
||||
if (model.startdate > model.enddate)
|
||||
{
|
||||
return BadRequest("掛單結束時間不能再開始時間之前");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
var guadanorder = new GuaDanOrder
|
||||
{
|
||||
StartDate = model.startdate,
|
||||
EndDate = model.enddate,
|
||||
CreateUser = model.createuser,
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedAt = DateTime.Now,
|
||||
Notes = model.note,
|
||||
GuaDanOrderNo = GuaDanOrder.GenerateStatusCode(),
|
||||
BookerName = model.bookerName,
|
||||
BookerPhone = model.bookerPhone,
|
||||
Uuid = Guid.NewGuid(),
|
||||
};
|
||||
_db.GuaDanOrder.Add(guadanorder);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(guadanorder);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/guadan/update")]
|
||||
public async Task<IHttpActionResult> updateGuadanOrder(GuaDanOrderView model)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return BadRequest("掛單資料不可為空");
|
||||
}
|
||||
if (!model.Uuid.HasValue)
|
||||
{
|
||||
return BadRequest("");
|
||||
}
|
||||
try
|
||||
{
|
||||
if (model.startdate > model.enddate)
|
||||
{
|
||||
return BadRequest("掛單結束時間不能再開始時間之前");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
var order = await _db.GuaDanOrder.FindAsync(model.Uuid.Value);
|
||||
if (order == null)
|
||||
{
|
||||
return BadRequest("未找到对应挂单资料");
|
||||
}
|
||||
order.StartDate = model.startdate;
|
||||
order.EndDate = model.enddate;
|
||||
order.Notes = model.note;
|
||||
order.BookerName = model.bookerName;
|
||||
order.BookerPhone = model.bookerPhone;
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(model);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/guadan/delete")]
|
||||
public async Task<IHttpActionResult> deleteGuadanOrder([FromUri] Guid uuid)
|
||||
{
|
||||
var guadan = await _db.GuaDanOrder.FindAsync(uuid);
|
||||
if (guadan == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
using (var transaction = _db.Database.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
var guadanGuests = await _db.GuaDanOrderGuest
|
||||
.Where(a => a.GuaDanOrderNo == guadan.GuaDanOrderNo)
|
||||
.ToListAsync();
|
||||
var scheduleIds = _db.RegionAndRoomAndBedSchedule
|
||||
.Where(a => a.GuaDanOrderNo == guadan.GuaDanOrderNo)
|
||||
.Where( b => b.IsActive == true)
|
||||
.Select(c => c.GuaDanOrderNo)
|
||||
.ToList();
|
||||
if (guadanGuests.Any())
|
||||
{
|
||||
_db.GuaDanOrderGuest.RemoveRange(guadanGuests);
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (scheduleIds.Any())
|
||||
{
|
||||
var schedules = await _db.RegionAndRoomAndBedSchedule
|
||||
.Where(a => scheduleIds.Contains(a.GuaDanOrderNo))
|
||||
.ToListAsync();
|
||||
|
||||
if (schedules.Any())
|
||||
_db.RegionAndRoomAndBedSchedule.RemoveRange(schedules);
|
||||
}
|
||||
_db.GuaDanOrder.Remove(guadan);
|
||||
await _db.SaveChangesAsync();
|
||||
transaction.Commit();
|
||||
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class guadan_order_dto
|
||||
{
|
||||
public int id { get; set; }
|
||||
public int room_id { get; set; }
|
||||
public int bed_id { get; set; }
|
||||
public int guest_id { get; set; }
|
||||
public DateTime start_date { get; set; }
|
||||
public DateTime? end_date { get; set; }
|
||||
public Guid? statusUuid { get; set; }
|
||||
public int? create_user { get; set; }
|
||||
public DateTime created_at { get; set; }
|
||||
public DateTime updated_at { get; set; }
|
||||
public string notes { get; set; }
|
||||
public int? follower_num { get; set; }
|
||||
|
||||
public Room room { get; set; } = null;
|
||||
public RegionRoomBed bed { get; set; } = null;
|
||||
}
|
||||
|
||||
}
|
||||
353
web/App_Code/api/guadanOrderGuestController.cs
Normal file
353
web/App_Code/api/guadanOrderGuestController.cs
Normal file
@@ -0,0 +1,353 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
|
||||
/// <summary>
|
||||
/// guadanOrderGuest 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class guadanOrderGuestController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
[HttpGet]
|
||||
[Route("api/guadanorderguest/get")]
|
||||
public async Task<IHttpActionResult> Get()
|
||||
{
|
||||
var data = await _db.GuaDanOrderGuest.ToListAsync();
|
||||
return Ok(data);
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("api/guadanorderguest/getbyorderno")]
|
||||
public async Task<IHttpActionResult> getByOrderNo(string orderNo)
|
||||
{
|
||||
// 先查数据库,不做格式化
|
||||
var qry = await _db.GuaDanOrderGuest
|
||||
.Where(a => a.GuaDanOrderNo == orderNo && a.IsDeleted == false)
|
||||
.ToListAsync();
|
||||
|
||||
// 拉到内存后再处理日期
|
||||
var data = qry.Select(a => new guadan_order_guest_display_dto
|
||||
{
|
||||
Uuid = a.Uuid,
|
||||
name = null,
|
||||
followerNum = a.FollowerNum,
|
||||
roomUuid = a.RoomUuid,
|
||||
bedUuid = a.BedUuid,
|
||||
checkinat = a.CheckInAt.HasValue ? a.CheckInAt.Value.ToString("yyyy-MM-dd") : null,
|
||||
checkoutat = a.CheckOutAt.HasValue ? a.CheckOutAt.Value.ToString("yyyy-MM-dd") : null,
|
||||
phone = null,
|
||||
roomName = a.Room.Name,
|
||||
bedName = a.RegionRoomBed.Name,
|
||||
orderNo = a.GuaDanOrderNo,
|
||||
follower = a.followers,
|
||||
statusUuid = a.statusUuid,
|
||||
statusName = a.RegionRoomBedStatus?.Name,
|
||||
}).ToList();
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/guadanorderguest/create")]
|
||||
public async Task<IHttpActionResult> create([FromBody] guadan_order_guest_dto model)
|
||||
{
|
||||
if (model == null)
|
||||
return BadRequest("");
|
||||
|
||||
// 驗證床位與蓮友
|
||||
if (model.followerNum.HasValue && model.bedUuid.HasValue)
|
||||
{
|
||||
var bed = _db.RegionRoomBed.Find(model.bedUuid.Value);
|
||||
var follower = _db.followers.Find(model.followerNum.Value);
|
||||
|
||||
if (bed == null || follower == null)
|
||||
return BadRequest("床位或蓮友不存在");
|
||||
|
||||
bool isMaleFollower;
|
||||
if (follower.sex == "男眾")
|
||||
isMaleFollower = true;
|
||||
else if (follower.sex == "女眾")
|
||||
isMaleFollower = false;
|
||||
else
|
||||
return BadRequest("蓮友性別未知");
|
||||
|
||||
if (bed.Gender != isMaleFollower)
|
||||
return BadRequest("床位性別與蓮友性別不同");
|
||||
}
|
||||
|
||||
if (!model.bedUuid.HasValue)
|
||||
return BadRequest("床位 UUID 不能为空");
|
||||
if (!model.checkInAt.HasValue)
|
||||
return BadRequest("入住时间不能为空");
|
||||
|
||||
// 長期占用處理:checkOutAt 可為 null
|
||||
DateTime? checkOut = model.checkOutAt.Value.Date;
|
||||
|
||||
if (checkOut.HasValue && model.checkInAt > checkOut)
|
||||
return BadRequest("掛單結束時間不能再開始時間之前");
|
||||
if(model.checkInAt == model.checkOutAt)
|
||||
{
|
||||
return BadRequest("掛單結束時間和開始時間不能是同一天");
|
||||
}
|
||||
// 檢查床位可用性
|
||||
var bedIsCanUse = await RegionAndRoomAndBedSchedule.IsBedAvailableAsync(
|
||||
_db,
|
||||
model.bedUuid.Value,
|
||||
model.checkInAt.Value.Date,
|
||||
checkOut
|
||||
);
|
||||
if (!bedIsCanUse)
|
||||
return BadRequest("床位在該時間段內已被占用");
|
||||
|
||||
// 建立掛單
|
||||
var guest = new GuaDanOrderGuest
|
||||
{
|
||||
GuaDanOrderNo = model.orderNo,
|
||||
FollowerNum = model.followerNum,
|
||||
RoomUuid = model.roomUuid,
|
||||
BedUuid = model.bedUuid,
|
||||
CheckInAt = model.checkInAt?.Date,
|
||||
CheckOutAt = checkOut,
|
||||
Uuid = Guid.NewGuid(),
|
||||
statusUuid = model.statusUuid,
|
||||
};
|
||||
|
||||
if (model.followerNum.HasValue)
|
||||
{
|
||||
if (_db.GuaDanOrderGuest.Any(a => a.FollowerNum == model.followerNum && a.GuaDanOrderNo == model.orderNo))
|
||||
return BadRequest("該蓮友已經在該掛單中");
|
||||
}
|
||||
|
||||
_db.GuaDanOrderGuest.Add(guest);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
// 生成每日排程
|
||||
if (checkOut.HasValue)
|
||||
{
|
||||
int totalDays = (checkOut.Value - model.checkInAt.Value.Date).Days;
|
||||
for (int i = 0; i < totalDays; i++)
|
||||
{
|
||||
var scheduleDate = model.checkInAt.Value.Date.AddDays(i);
|
||||
|
||||
var schedul = new RegionAndRoomAndBedSchedule
|
||||
{
|
||||
Title = "掛單",
|
||||
Description = "床位掛單",
|
||||
ScheduleDate = scheduleDate,
|
||||
IsDeleted = false,
|
||||
IsActive = true,
|
||||
TargetUuid = guest.BedUuid,
|
||||
UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation,
|
||||
CreatedAt = DateTime.Now,
|
||||
GuaDanOrderNo = guest.GuaDanOrderNo,
|
||||
Uuid = Guid.NewGuid()
|
||||
};
|
||||
_db.RegionAndRoomAndBedSchedule.Add(schedul);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 長期占用,ScheduleDate = null
|
||||
var schedul = new RegionAndRoomAndBedSchedule
|
||||
{
|
||||
Title = "掛單",
|
||||
Description = "床位掛單(長期占用)",
|
||||
ScheduleDate = null,
|
||||
IsDeleted = false,
|
||||
IsActive = true,
|
||||
TargetUuid = guest.BedUuid,
|
||||
UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation,
|
||||
CreatedAt = DateTime.Now,
|
||||
GuaDanOrderNo = guest.GuaDanOrderNo,
|
||||
Uuid = Guid.NewGuid()
|
||||
};
|
||||
_db.RegionAndRoomAndBedSchedule.Add(schedul);
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/guadanorderguest/update")]
|
||||
public async Task<IHttpActionResult> update([FromBody] guadan_order_guest_dto model)
|
||||
{
|
||||
if (model == null)
|
||||
return BadRequest("");
|
||||
|
||||
// 驗證床位與蓮友
|
||||
if (model.followerNum.HasValue && model.bedUuid.HasValue)
|
||||
{
|
||||
var bed = _db.RegionRoomBed.Find(model.bedUuid.Value);
|
||||
var follower = _db.followers.Find(model.followerNum.Value);
|
||||
|
||||
if (bed == null || follower == null)
|
||||
return BadRequest("床位或蓮友不存在");
|
||||
|
||||
bool isMaleFollower;
|
||||
if (follower.sex == "男眾") isMaleFollower = true;
|
||||
else if (follower.sex == "女眾") isMaleFollower = false;
|
||||
else return BadRequest("蓮友性別未知");
|
||||
|
||||
if (bed.Gender != isMaleFollower)
|
||||
return BadRequest("床位性別與蓮友性別不同");
|
||||
}
|
||||
|
||||
if (!model.bedUuid.HasValue)
|
||||
return BadRequest("床位 UUID 不能为空");
|
||||
|
||||
if (!model.checkInAt.HasValue)
|
||||
return BadRequest("入住时间不能为空");
|
||||
|
||||
// 長期占用處理
|
||||
DateTime? checkOut = model.checkOutAt?.Date;
|
||||
if (checkOut.HasValue && model.checkInAt > checkOut)
|
||||
return BadRequest("掛單結束時間不能再開始時間之前");
|
||||
|
||||
var guest = await _db.GuaDanOrderGuest.FindAsync(model.Uuid);
|
||||
if (guest == null) return BadRequest();
|
||||
|
||||
// 檢查床位可用性
|
||||
var bedIsCanUse = await RegionAndRoomAndBedSchedule.IsBedAvailableAsync(
|
||||
_db,
|
||||
model.bedUuid.Value,
|
||||
model.checkInAt.Value.Date,
|
||||
checkOut
|
||||
);
|
||||
|
||||
if (!bedIsCanUse && guest.BedUuid != model.bedUuid)
|
||||
return BadRequest("床位在該時間段內已被占用");
|
||||
|
||||
if (model.followerNum.HasValue)
|
||||
{
|
||||
bool exists = await _db.GuaDanOrderGuest
|
||||
.Where(a => a.FollowerNum == model.followerNum
|
||||
&& a.GuaDanOrderNo == model.orderNo
|
||||
&& a.Uuid != model.Uuid)
|
||||
.AnyAsync();
|
||||
if (exists) return BadRequest("該蓮友已經在該掛單中");
|
||||
}
|
||||
|
||||
// 更新掛單基本資料
|
||||
guest.FollowerNum = model.followerNum;
|
||||
guest.RoomUuid = model.roomUuid;
|
||||
guest.BedUuid = model.bedUuid;
|
||||
guest.CheckInAt = model.checkInAt?.Date;
|
||||
guest.CheckOutAt = checkOut;
|
||||
guest.statusUuid = model.statusUuid;
|
||||
|
||||
// 刪除原有每日排程
|
||||
var oldSchedules = _db.RegionAndRoomAndBedSchedule
|
||||
.Where(s => s.GuaDanOrderNo == guest.GuaDanOrderNo)
|
||||
.ToList();
|
||||
_db.RegionAndRoomAndBedSchedule.RemoveRange(oldSchedules);
|
||||
|
||||
// 重新生成每日排程
|
||||
if (checkOut.HasValue)
|
||||
{
|
||||
int totalDays = (checkOut.Value - model.checkInAt.Value.Date).Days;
|
||||
for (int i = 0; i < totalDays; i++)
|
||||
{
|
||||
var date = model.checkInAt.Value.Date.AddDays(i);
|
||||
var schedul = new RegionAndRoomAndBedSchedule
|
||||
{
|
||||
Title = "掛單",
|
||||
Description = "床位掛單",
|
||||
ScheduleDate = date,
|
||||
IsDeleted = false,
|
||||
IsActive = true,
|
||||
TargetUuid = guest.BedUuid,
|
||||
UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation,
|
||||
CreatedAt = DateTime.Now,
|
||||
GuaDanOrderNo = guest.GuaDanOrderNo,
|
||||
Uuid = Guid.NewGuid()
|
||||
};
|
||||
_db.RegionAndRoomAndBedSchedule.Add(schedul);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 長期占用
|
||||
var schedul = new RegionAndRoomAndBedSchedule
|
||||
{
|
||||
Title = "掛單",
|
||||
Description = "床位掛單(長期占用)",
|
||||
ScheduleDate = null,
|
||||
IsDeleted = false,
|
||||
IsActive = true,
|
||||
TargetUuid = guest.BedUuid,
|
||||
UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation,
|
||||
CreatedAt = DateTime.Now,
|
||||
GuaDanOrderNo = guest.GuaDanOrderNo,
|
||||
Uuid = Guid.NewGuid()
|
||||
};
|
||||
_db.RegionAndRoomAndBedSchedule.Add(schedul);
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/guadanorderguest/delete")]
|
||||
public async Task<IHttpActionResult> deleteGuadanGuest([FromUri] Guid uuid)
|
||||
{
|
||||
var guest = await _db.GuaDanOrderGuest.FindAsync(uuid);
|
||||
if (guest == null)
|
||||
return BadRequest("未找到指定挂单资料");
|
||||
|
||||
// 删除所有与该 guest 相关的排程(每日排程或長期占用)
|
||||
var schedules = _db.RegionAndRoomAndBedSchedule
|
||||
.Where(s => s.GuaDanOrderNo == guest.GuaDanOrderNo)
|
||||
.ToList();
|
||||
|
||||
if (schedules.Any())
|
||||
_db.RegionAndRoomAndBedSchedule.RemoveRange(schedules);
|
||||
|
||||
_db.GuaDanOrderGuest.Remove(guest);
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
return Ok(new { message = "删除成功" });
|
||||
}
|
||||
|
||||
public class guadan_order_guest_dto
|
||||
{
|
||||
public Guid? Uuid { get; set; }
|
||||
public int? followerNum { get; set; }
|
||||
public string orderNo { get; set; }
|
||||
public Guid? roomUuid { get; set; }
|
||||
public Guid? bedUuid { get; set; }
|
||||
public DateTime? checkInAt { get; set; }
|
||||
public DateTime? checkOutAt { get; set; }
|
||||
public Guid? statusUuid { get; set; }
|
||||
}
|
||||
public class guadan_order_guest_display_dto
|
||||
{
|
||||
public Guid? Uuid { get; set; }
|
||||
public int? followerNum { get; set; }
|
||||
public string orderNo { get; set; }
|
||||
public string name { get; set; }
|
||||
public Guid? roomUuid { get; set; }
|
||||
public Guid? bedUuid { get;set; }
|
||||
public string checkinat { get;set; }
|
||||
public string checkoutat { get;set; }
|
||||
public int? gender { get; set; }
|
||||
public Guid? statusUuid { get; set; }
|
||||
public string statusName { get; set; }
|
||||
public string phone { get; set; }
|
||||
public string note { get; set; }
|
||||
public string roomName { get; set; }
|
||||
public string bedName { get; set; }
|
||||
public follower follower { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
72
web/App_Code/api/guadanStatisticsController.cs
Normal file
72
web/App_Code/api/guadanStatisticsController.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
|
||||
/// <summary>
|
||||
/// guadanStatisticsController 的摘要描述
|
||||
/// </summary>
|
||||
public class guadanStatisticsController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
|
||||
[HttpGet]
|
||||
[Route("api/guadanStatistics/GetGuadanStatistics")]
|
||||
public async Task<IHttpActionResult> GetGuadanStatistics()
|
||||
{
|
||||
//挂单统计:房间,床位,挂单笔数,挂单人数的统计
|
||||
|
||||
var now = DateTime.Now;
|
||||
var roomCount = await _db.Room.Where(a => a.IsDeleted == false).CountAsync();
|
||||
var rooms = await _db.Room.Include(r => r.RegionRoomBed).ToListAsync();
|
||||
|
||||
var emptyRoomCount = rooms
|
||||
.Where(r => r.RegionRoomBed.All(b => b.IsAvailableDuring(now, now, _db))) // 這裡就能用方法
|
||||
.Count();
|
||||
var bedCount = await _db.RegionRoomBed.Where(a => a.IsDeleted == false).CountAsync();
|
||||
var maleBedCount = await _db.RegionRoomBed.Where(a => a.IsDeleted == false && a.Gender == true).CountAsync();
|
||||
var femaleBedCount = await _db.RegionRoomBed.Where(a => a.IsDeleted == false && a.Gender == false).CountAsync();
|
||||
|
||||
var guadanTotalCount = await _db.GuaDanOrder.Where(a => a.IsDeleted == false).CountAsync();
|
||||
var guadanPeopleTotal = await _db.GuaDanOrderGuest.Where(a => a.IsDeleted == false).CountAsync();
|
||||
var guadanPeopleMale = await _db.GuaDanOrderGuest.Where(a => a.IsDeleted == false && a.followers.sex == "男眾").CountAsync();
|
||||
var guadanPeopleFemale = await _db.GuaDanOrderGuest.Where(a => a.IsDeleted == false && a.followers.sex == "女眾").CountAsync();
|
||||
dynamic bedCounts = await RegionAndRoomAndBedSchedule.GetAvailableBedCountsAsync(_db, DateTime.Now, DateTime.Now);
|
||||
var guadanCurrentCount = await _db.GuaDanOrder.Where(a => now < a.EndDate).CountAsync();
|
||||
var guadanPeopleCurrent = await _db.GuaDanOrderGuest.Where( a => a.CheckOutAt > now).CountAsync();
|
||||
var guadanPeopleCurrentMale = await _db.GuaDanOrderGuest.Where(a => a.CheckOutAt > now && a.followers.sex == "男眾").CountAsync();
|
||||
var guadanPeopleCurrentFemale = await _db.GuaDanOrderGuest.Where(a => a.CheckOutAt > now && a.followers.sex == "女眾").CountAsync();
|
||||
|
||||
var result = new
|
||||
{
|
||||
roomStatistics = new
|
||||
{
|
||||
roomCount = roomCount,
|
||||
emptyRoomCount = emptyRoomCount,
|
||||
bedCount = bedCount,
|
||||
maleBedCount = maleBedCount,
|
||||
femaleBedCount = femaleBedCount,
|
||||
emptyBedCount = bedCounts.male + bedCounts.female,
|
||||
emptyMaleBedCount = bedCounts.male,
|
||||
emptyFemaleBedCount = bedCounts.female
|
||||
},
|
||||
guadanStatistics = new
|
||||
{
|
||||
guadanTotalCount = guadanTotalCount, // 总挂单次数
|
||||
guadanCurrentCount = guadanCurrentCount, // 当前挂单数量
|
||||
guadanPeopleTotal = guadanPeopleTotal, // 总挂单人数
|
||||
guadanPeopleMale = guadanPeopleMale,
|
||||
guadanPeopleFemale = guadanPeopleFemale,
|
||||
guadanPeopleCurrent = guadanPeopleCurrent, // 当前挂单人数
|
||||
guadanPeopleCurrentMale = guadanPeopleCurrentMale,
|
||||
guadanPeopleCurrentFemale = guadanPeopleCurrentFemale
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
31
web/App_Code/api/lianyouController.cs
Normal file
31
web/App_Code/api/lianyouController.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using PagedList;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
|
||||
/// <summary>
|
||||
/// lianyouController 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class lianyouController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/lianyou/getfollowers")]
|
||||
public async Task<IHttpActionResult> GetGuadanFollowers(int page, int pageSize, string searchName = null)
|
||||
{
|
||||
var qry = _db.followers.AsEnumerable();
|
||||
if(searchName != null)
|
||||
{
|
||||
qry = qry.Where(a => (a.u_name ?? "").Contains(searchName) || (a.phone ?? "").Contains(searchName));
|
||||
}
|
||||
var count = qry.Count();
|
||||
var data = qry.OrderBy(a => a.f_number).ToPagedList(page, pageSize);
|
||||
return Ok(new {data = data, count = count});
|
||||
}
|
||||
}
|
||||
460
web/App_Code/api/regionController.cs
Normal file
460
web/App_Code/api/regionController.cs
Normal file
@@ -0,0 +1,460 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using System.Web.Routing;
|
||||
|
||||
/// <summary>
|
||||
/// regionController 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class regionController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
public regionController()
|
||||
{
|
||||
//
|
||||
// TODO: 在這裡新增建構函式邏輯
|
||||
//
|
||||
}
|
||||
|
||||
public class RoomFilter
|
||||
{
|
||||
public DateTime StartDate { get; set; } = DateTime.Now.Date;
|
||||
public DateTime EndDate { get; set; } = DateTime.Today.AddDays(1);
|
||||
public Guid? AreaId { get; set; } = null;
|
||||
public Guid? RoomId { get; set; } = null;
|
||||
public bool Occupied { get; set; }
|
||||
public bool Unoccupied { get; set; }
|
||||
public bool? Gender { get; set; } = null;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/list")]
|
||||
public IHttpActionResult GetList([FromBody] RoomFilter filter)
|
||||
{
|
||||
var startDate = filter.StartDate.Date;
|
||||
var endDate = filter.EndDate.Date;
|
||||
|
||||
var query = _db.Region
|
||||
.Where(r => !r.IsDeleted)
|
||||
.Where(r => r.Room.Any());
|
||||
|
||||
if (filter.Gender != null)
|
||||
{
|
||||
query = query.Where(a => a.Gender == filter.Gender || a.Gender == null);
|
||||
}
|
||||
|
||||
// 區域過濾
|
||||
if (filter.AreaId.HasValue && filter.AreaId.Value != Guid.Empty)
|
||||
{
|
||||
query = query.Where(r => r.Uuid == filter.AreaId.Value);
|
||||
}
|
||||
|
||||
// 房間過濾
|
||||
if (filter.RoomId.HasValue && filter.RoomId.Value != Guid.Empty)
|
||||
{
|
||||
query = query.Where(r => r.Room.Any(room => room.Uuid == filter.RoomId.Value));
|
||||
}
|
||||
|
||||
var data = query
|
||||
.ToList()
|
||||
.Select(r => new
|
||||
{
|
||||
r.Uuid,
|
||||
r.Name,
|
||||
regionPath = r.Name,
|
||||
Room = r.Room
|
||||
.Where(room => filter.Gender == null || room.Gender == filter.Gender)
|
||||
.Where(room =>
|
||||
!filter.Unoccupied || room.RegionRoomBed.Any(bed => bed.IsAvailableDuring(startDate, endDate, _db))
|
||||
)
|
||||
.Where(room => !filter.RoomId.HasValue || room.Uuid == filter.RoomId.Value)
|
||||
.Select(room => new
|
||||
{
|
||||
gender = room.Gender,
|
||||
room.Uuid,
|
||||
room.Name,
|
||||
Stats = new
|
||||
{
|
||||
BedCount = room.RegionRoomBed.Count(),
|
||||
AvailableCount = room.RegionRoomBed.Count(b => b.IsAvailableDuring(startDate, endDate, _db)),
|
||||
},
|
||||
beds = room.RegionRoomBed
|
||||
.ToList()
|
||||
.Where(bed =>
|
||||
(!filter.Occupied || !bed.IsAvailableDuring(startDate, endDate, _db)) &&
|
||||
(!filter.Unoccupied || bed.IsAvailableDuring(startDate, endDate, _db))
|
||||
)
|
||||
.Select(bed => new
|
||||
{
|
||||
bed.Uuid,
|
||||
bed.Name,
|
||||
bed.Gender,
|
||||
bed.RoomUuid,
|
||||
bed.StatusUuid,
|
||||
bed.IsActive,
|
||||
bed.IsDeleted,
|
||||
canuse = bed.IsAvailableDuring(startDate, endDate, _db),
|
||||
statusname = bed.RegionRoomBedStatus.Name,
|
||||
schedules = _db.RegionAndRoomAndBedSchedule
|
||||
.Where(s => s.TargetUuid == bed.Uuid
|
||||
&& s.IsDeleted == false
|
||||
&& s.IsActive
|
||||
&& (s.ScheduleDate == null
|
||||
|| (s.ScheduleDate >= startDate && s.ScheduleDate <= endDate)))
|
||||
.Select(s => new
|
||||
{
|
||||
s.Uuid,
|
||||
scheduledate = s.ScheduleDate,
|
||||
s.UseType,
|
||||
s.Title,
|
||||
s.Description,
|
||||
s.GuaDanOrderNo
|
||||
})
|
||||
.ToList()
|
||||
}),
|
||||
}).ToList()
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// 過濾掉沒有房間的區域
|
||||
data = data.Where(a => a.Room.Count() > 0).ToList();
|
||||
|
||||
var summary = new
|
||||
{
|
||||
TotalBeds = _db.RegionRoomBed.Count(a => !a.IsDeleted),
|
||||
TotalMaleBeds = _db.RegionRoomBed.Count(a => !a.IsDeleted && a.Gender == true),
|
||||
TotalRooms = _db.Room.Count(a => !a.IsDeleted),
|
||||
TotalMaleRooms = _db.Room.Count(a => !a.IsDeleted && a.Gender == true),
|
||||
};
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Regions = data,
|
||||
Summary = summary,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 遞迴生成區域完整路徑
|
||||
/// </summary>
|
||||
private string BuildRegionPath(Region region)
|
||||
{
|
||||
string path = region.Name;
|
||||
var parent = region.Region2; // 假設 Region2 是父區域導航屬性
|
||||
while (parent != null)
|
||||
{
|
||||
path = parent.Name + " > " + path;
|
||||
parent = parent.Region2;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/getRegionList")]
|
||||
public IHttpActionResult getRegionList()
|
||||
{
|
||||
var allRegions = _db.Region.ToList();
|
||||
|
||||
var rootRegions = allRegions
|
||||
.Where(r => r.ParentUuid == null)
|
||||
.OrderBy(r => r.SortOrder)
|
||||
.ToList();
|
||||
|
||||
var tree = rootRegions
|
||||
.Select(r => BuildRegionDto(r, allRegions))
|
||||
.ToList();
|
||||
return Ok(tree);
|
||||
}
|
||||
private RegionDto BuildRegionDto(Region region, List<Region> allRegions)
|
||||
{
|
||||
return new RegionDto
|
||||
{
|
||||
Uuid = region.Uuid,
|
||||
Name = region.Name,
|
||||
Description = region.Description,
|
||||
SortOrder = region.SortOrder,
|
||||
ParentUuid = region.ParentUuid,
|
||||
RegionTypeUuid = region.RegionTypeUuid,
|
||||
IsActive = region.IsActive,
|
||||
RoomCount = region.RoomCount,
|
||||
BedDto = new List<BedDto>(),
|
||||
Rooms = region.Room.Select(a => new RoomDto {
|
||||
Uuid = a.Uuid,
|
||||
Name = a.Name,
|
||||
RegionUuid = a.RegionUuid,
|
||||
Gender = a.Gender,
|
||||
BedCount = a.BedCount,
|
||||
IsActive = a.IsActive,
|
||||
beds = a.RegionRoomBed.Select(c => new BedDto
|
||||
{
|
||||
Uuid = c.Uuid,
|
||||
name = c.Name,
|
||||
roomUuid = c.RoomUuid,
|
||||
isactive = c.IsActive,
|
||||
statusuuid = c.StatusUuid,
|
||||
Gender = c.Gender,
|
||||
|
||||
}).ToList(),
|
||||
|
||||
}).ToList(),
|
||||
Children = allRegions
|
||||
.Where(r => r.ParentUuid == region.Uuid)
|
||||
.OrderBy(r => r.SortOrder)
|
||||
.Select(child => BuildRegionDto(child, allRegions))
|
||||
.ToList(),
|
||||
Gender = region.Gender,
|
||||
};
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/getRegionListByGender")]
|
||||
public IHttpActionResult getRegionListByGender([FromBody] GenderRequest request)
|
||||
{
|
||||
var allRegions = _db.Region.ToList();
|
||||
|
||||
// 根区域
|
||||
var rootRegions = allRegions
|
||||
.Where(r => r.ParentUuid == null)
|
||||
.OrderBy(r => r.SortOrder)
|
||||
.ToList();
|
||||
|
||||
// 生成树并按性别过滤
|
||||
var tree = rootRegions
|
||||
.Select(r => BuildRegionDtoByGender(r, allRegions, request.IsMale))
|
||||
.Where(r => r != null) // 去掉没有房间的区域
|
||||
.ToList();
|
||||
|
||||
return Ok(tree);
|
||||
}
|
||||
|
||||
// 根据性别过滤房间的 BuildRegionDto
|
||||
private RegionDto BuildRegionDtoByGender(Region region, List<Region> allRegions, bool? gender)
|
||||
{
|
||||
// 过滤房间按性别
|
||||
var rooms = region.Room?
|
||||
.Where(a => !gender.HasValue || a.Gender == gender.Value)
|
||||
.Select(a => new RoomDto
|
||||
{
|
||||
Uuid = a.Uuid,
|
||||
Name = a.Name,
|
||||
RegionUuid = a.RegionUuid,
|
||||
Gender = a.Gender,
|
||||
BedCount = a.BedCount,
|
||||
IsActive = a.IsActive,
|
||||
beds = a.RegionRoomBed.Select(c => new BedDto
|
||||
{
|
||||
Uuid = c.Uuid,
|
||||
name = c.Name,
|
||||
roomUuid = c.RoomUuid,
|
||||
isactive = c.IsActive,
|
||||
statusuuid = c.StatusUuid
|
||||
}).ToList()
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// 递归生成子区域
|
||||
var children = allRegions
|
||||
.Where(r => r.ParentUuid == region.Uuid)
|
||||
.Select(child => BuildRegionDtoByGender(child, allRegions, gender))
|
||||
.Where(c => c != null) // 去掉没有房间的子区域
|
||||
.ToList();
|
||||
|
||||
// 如果这个区域既没有房间也没有子区域,则返回 null
|
||||
if (!rooms.Any() && !children.Any())
|
||||
return null;
|
||||
|
||||
return new RegionDto
|
||||
{
|
||||
Uuid = region.Uuid,
|
||||
Name = region.Name,
|
||||
Description = region.Description,
|
||||
SortOrder = region.SortOrder,
|
||||
ParentUuid = region.ParentUuid,
|
||||
RegionTypeUuid = region.RegionTypeUuid,
|
||||
IsActive = region.IsActive,
|
||||
RoomCount = rooms.Count,
|
||||
Rooms = rooms,
|
||||
BedDto = new List<BedDto>(),
|
||||
Children = children,
|
||||
Gender = region.Gender,
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// 请求模型
|
||||
public class GenderRequest
|
||||
{
|
||||
public bool? IsMale { get; set; } // true = 男, false = 女, null = 不过滤
|
||||
}
|
||||
|
||||
public class RoomDto
|
||||
{
|
||||
public Guid Uuid { get; set; }
|
||||
public Guid RegionUuid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Gender { get; set; }
|
||||
public Nullable<int> BedCount { get; set; }
|
||||
public Nullable<bool> IsActive { get; set; }
|
||||
public Nullable<System.DateTime> CreatedAt { get; set; }
|
||||
public Nullable<System.DateTime> UpdatedAt { get; set; }
|
||||
public List<BedDto> beds { get; set; }
|
||||
}
|
||||
public class BedDto {
|
||||
public Guid Uuid { get; set; }
|
||||
public Guid? roomUuid { get; set; }
|
||||
public string name { get; set; }
|
||||
public Guid? statusuuid { get; set; }
|
||||
public bool isactive { get; set; }
|
||||
public bool Gender { get; set; }
|
||||
}
|
||||
|
||||
public class RegionDto
|
||||
{
|
||||
public Guid Uuid { get; set; } = Guid.NewGuid();
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int? SortOrder { get; set; }
|
||||
public Guid? ParentUuid { get; set; }
|
||||
public List<RegionDto> Children { get; set; }
|
||||
public Guid? RegionTypeUuid { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public int? RoomCount { get; set; }
|
||||
public List<RoomDto> Rooms { get; set; }
|
||||
public List<BedDto> BedDto { get; set; }
|
||||
public bool? Gender { get; set; }
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/create")]
|
||||
public IHttpActionResult createRegion([FromBody] RegionDto dto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Name))
|
||||
return BadRequest("區域名稱為必填");
|
||||
var region = new Region
|
||||
{
|
||||
Name = dto.Name,
|
||||
Description = dto.Description,
|
||||
SortOrder = dto.SortOrder,
|
||||
ParentUuid = dto.ParentUuid,
|
||||
RegionTypeUuid = dto.RegionTypeUuid,
|
||||
IsActive = dto.IsActive,
|
||||
RoomCount = dto.RoomCount,
|
||||
Uuid = Guid.NewGuid(),
|
||||
};
|
||||
_db.Region.Add(region);
|
||||
_db.SaveChanges();
|
||||
return Ok(new { message = "新增成功", id = region.Uuid });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/update")]
|
||||
public IHttpActionResult updateRegion([FromBody] RegionDto dto)
|
||||
{
|
||||
if (dto == null || dto.Uuid == Guid.Empty)
|
||||
return BadRequest("無效的區域資料");
|
||||
if (string.IsNullOrWhiteSpace(dto.Name))
|
||||
return BadRequest("區域名稱為必填");
|
||||
var region = _db.Region.FirstOrDefault(r => r.Uuid == dto.Uuid);
|
||||
if (region == null)
|
||||
return NotFound();
|
||||
region.Name = dto.Name;
|
||||
region.Description = dto.Description;
|
||||
region.SortOrder = dto.SortOrder;
|
||||
region.ParentUuid = dto.ParentUuid;
|
||||
region.UpdatedAt = DateTime.Now;
|
||||
region.RegionTypeUuid = dto.RegionTypeUuid;
|
||||
region.IsActive = dto.IsActive;
|
||||
region.RoomCount = dto.RoomCount;
|
||||
_db.SaveChanges();
|
||||
return Ok(new { message = "更新成功", id = region.Uuid });
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/delete")]
|
||||
public IHttpActionResult deleteRegion([FromBody] RegionDto dto)
|
||||
{
|
||||
if (dto == null || dto.Uuid == Guid.Empty)
|
||||
return BadRequest("無效的區域ID");
|
||||
|
||||
var region = _db.Region.Find(dto.Uuid);
|
||||
if (region == null)
|
||||
return NotFound();
|
||||
|
||||
// 先找出所有子節點(直屬)
|
||||
var childRegions = _db.Region.Where(r => r.ParentUuid == region.Uuid).ToList();
|
||||
|
||||
// 如果你要遞迴刪除,需實作遞迴刪除所有子節點
|
||||
foreach (var child in childRegions)
|
||||
{
|
||||
DeleteRegionRecursive(child);
|
||||
}
|
||||
|
||||
// 最後刪除自己
|
||||
_db.Region.Remove(region);
|
||||
_db.SaveChanges();
|
||||
|
||||
return Ok(new { message = "刪除成功" });
|
||||
}
|
||||
|
||||
// 遞迴刪除子節點
|
||||
private void DeleteRegionRecursive(Region region)
|
||||
{
|
||||
var children = _db.Region.Where(r => r.ParentUuid == region.Uuid).ToList();
|
||||
foreach (var child in children)
|
||||
{
|
||||
DeleteRegionRecursive(child);
|
||||
}
|
||||
_db.Region.Remove(region);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/getRegionType")]
|
||||
public IHttpActionResult getRegionType()
|
||||
{
|
||||
var data = _db.RegionType.Where(a => a.IsActive == true).ToList();
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("api/region/regionwithroom")]
|
||||
public IHttpActionResult GetRegionWithRoom()
|
||||
{
|
||||
//返回有房间的region
|
||||
var data = _db.Region.Where(a => a.Room.Count() > 0)
|
||||
.Select(r => new
|
||||
{
|
||||
r.Uuid,
|
||||
r.Name,
|
||||
r.ParentUuid,
|
||||
r.Gender,
|
||||
rooms = r.Room.Select(room => new
|
||||
{
|
||||
room.Uuid, room.Name, room.RegionUuid
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
return Ok(data);
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("api/room/roomwithbed")]
|
||||
public IHttpActionResult GetRoomWithBed(Guid? RegionUuid = null)
|
||||
{
|
||||
//获取所有有床位的房间
|
||||
var query = _db.Room
|
||||
.Select(r => new
|
||||
{
|
||||
r.Uuid,
|
||||
r.Name,
|
||||
r.RegionUuid
|
||||
});
|
||||
|
||||
if (RegionUuid.HasValue)
|
||||
{
|
||||
query = query.Where(r => r.RegionUuid == RegionUuid.Value);
|
||||
}
|
||||
|
||||
var rooms = query.ToList();
|
||||
return Ok(rooms);
|
||||
}
|
||||
}
|
||||
435
web/App_Code/api/regionRoomBedController.cs
Normal file
435
web/App_Code/api/regionRoomBedController.cs
Normal file
@@ -0,0 +1,435 @@
|
||||
using DocumentFormat.OpenXml.EMMA;
|
||||
using DocumentFormat.OpenXml.Office.CustomUI;
|
||||
using DocumentFormat.OpenXml.Wordprocessing;
|
||||
using Microsoft.AspNet.SignalR;
|
||||
using Model;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
|
||||
/// <summary>
|
||||
/// regionRoomBedController 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class regionRoomBedController : ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
public regionRoomBedController()
|
||||
{
|
||||
//
|
||||
// TODO: 在這裡新增建構函式邏輯
|
||||
//
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("api/region/room/bed/list")]
|
||||
public IHttpActionResult GetRegionRoomBedListByRoomId(Guid? roomUuid, DateTime StartTime, DateTime? EndTime)
|
||||
{
|
||||
if (StartTime == null || EndTime == null)
|
||||
return BadRequest("时间不能为空");
|
||||
|
||||
if (roomUuid == null)
|
||||
return BadRequest("roomId不能为空");
|
||||
|
||||
// 先取出床位
|
||||
var beds = _db.RegionRoomBed
|
||||
.Where(a => a.RoomUuid == roomUuid)
|
||||
.ToList();
|
||||
|
||||
// 取出所有相关排程
|
||||
var schedules = _db.RegionAndRoomAndBedSchedule
|
||||
.Where(b => b.IsDeleted == false && b.IsActive)
|
||||
.ToList();
|
||||
|
||||
var data = beds.Select(a =>
|
||||
{
|
||||
// 在内存中处理日期比较
|
||||
var bedSchedules = schedules
|
||||
.Where(b => b.TargetUuid == a.Uuid
|
||||
&& (b.ScheduleDate == null // 长期占用
|
||||
|| (b.ScheduleDate >= StartTime.Date && b.ScheduleDate <= EndTime.Value.Date)))
|
||||
.Select(c => new
|
||||
{
|
||||
c.Uuid,
|
||||
c.Description,
|
||||
c.IsDeleted,
|
||||
c.IsActive,
|
||||
c.GuaDanOrderNo,
|
||||
c.UseType,
|
||||
c.Title,
|
||||
c.TargetUuid,
|
||||
scheduledate = c.ScheduleDate,
|
||||
})
|
||||
.ToList();
|
||||
|
||||
bool canUsed = !bedSchedules.Any();
|
||||
|
||||
return new
|
||||
{
|
||||
a.Uuid,
|
||||
a.Name,
|
||||
a.Gender,
|
||||
a.IsActive,
|
||||
a.StatusUuid,
|
||||
a.RoomUuid,
|
||||
canUsed,
|
||||
schedule = bedSchedules
|
||||
};
|
||||
});
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/bed/create")]
|
||||
public IHttpActionResult CreateBed([FromBody] RegionRoomBed bed)
|
||||
{
|
||||
var room = _db.Room.Find(bed.RoomUuid);
|
||||
if (room == null)
|
||||
{
|
||||
return BadRequest("當前客房不存在");
|
||||
}
|
||||
if(room.Gender != bed.Gender)
|
||||
{
|
||||
return BadRequest("床為性別和房間性別必須一致");
|
||||
}
|
||||
if (room.BedCount != null && room.BedCount < room.RegionRoomBed.Count() + 1)
|
||||
{
|
||||
return BadRequest("該客房床位已滿");
|
||||
}
|
||||
var regionBed = new RegionRoomBed
|
||||
{
|
||||
Name = bed.Name,
|
||||
RoomUuid = bed.RoomUuid,
|
||||
StatusUuid = bed.StatusUuid,
|
||||
IsActive = bed.IsActive,
|
||||
Gender = bed.Gender,
|
||||
Uuid = Guid.NewGuid(),
|
||||
};
|
||||
_db.RegionRoomBed.Add(regionBed);
|
||||
_db.SaveChanges();
|
||||
|
||||
//創建床位
|
||||
return Ok(new {
|
||||
uuid = regionBed.Uuid,
|
||||
roomUuid = regionBed.RoomUuid,
|
||||
statusuuid = regionBed.StatusUuid,
|
||||
isactive = regionBed.IsActive,
|
||||
gender = regionBed.Gender,
|
||||
name = regionBed.Name,
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/bed/update")]
|
||||
public IHttpActionResult UpdateBed([FromBody] RegionRoomBed bed)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(bed.Name))
|
||||
{
|
||||
return BadRequest("床位名稱不可為空");
|
||||
}
|
||||
var oldBed = _db.RegionRoomBed.Find(bed.Uuid);
|
||||
if (oldBed == null)
|
||||
{
|
||||
return BadRequest("更新失敗,床位不存在");
|
||||
}
|
||||
var room = _db.Room.Find(bed.RoomUuid);
|
||||
if (room == null)
|
||||
{
|
||||
return BadRequest("當前客房不存在");
|
||||
}
|
||||
if (room.Gender != bed.Gender)
|
||||
{
|
||||
return BadRequest("床為性別和房間性別必須一致");
|
||||
}
|
||||
oldBed.StatusUuid = bed.StatusUuid;
|
||||
oldBed.IsActive = bed.IsActive;
|
||||
oldBed.Name = bed.Name;
|
||||
oldBed.Gender = bed.Gender;
|
||||
_db.SaveChanges();
|
||||
|
||||
//創建床位
|
||||
return Ok(new { message = "床位更新成功" });
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/bed/delete")]
|
||||
public IHttpActionResult delete([FromUri] Guid uuid)
|
||||
{
|
||||
var bed = _db.RegionRoomBed.Find(uuid);
|
||||
if (bed == null)
|
||||
{
|
||||
return BadRequest("未找到床位");
|
||||
}
|
||||
_db.RegionRoomBed.Remove(bed);
|
||||
_db.SaveChanges();
|
||||
return Ok(new { message = "刪除成功" });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("api/region/bed/getavailablebedcountbytime")]
|
||||
public async Task<IHttpActionResult> GetCanUseBedCountByTime(DateTime startTime, DateTime endTime)
|
||||
{
|
||||
//获取某个时间段内可用床位数量
|
||||
var counts = await RegionAndRoomAndBedSchedule.GetAvailableBedCountsAsync(_db, startTime, endTime);
|
||||
return Ok(counts);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/bed/preallocation")]
|
||||
public async Task<IHttpActionResult> PreAutoAllocationBed([FromBody] ConfirmAllocationRequest request)
|
||||
{
|
||||
DateTime allocationStart = request.CheckInAt;
|
||||
DateTime? allocationEnd = request.CheckOutAt;
|
||||
bool isAllallocation = true; //是否全部分配完毕
|
||||
// 获取可用床位列表
|
||||
var availableBeds = await RegionAndRoomAndBedSchedule.GetAvailableBedsAsync(_db, allocationStart, allocationEnd);
|
||||
|
||||
// 将床位按房间分组
|
||||
var roomGroups = availableBeds
|
||||
.GroupBy(b => b.RoomUuid)
|
||||
.Select(g => new
|
||||
{
|
||||
RoomUuid = g.Key,
|
||||
Beds = g.ToList(),
|
||||
Gender = g.First().Room.Gender // bool,不可空
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var data = new List<FollowerBedResponse>();
|
||||
|
||||
// 按性别分组请求
|
||||
var maleFollowers = request.PreBeds.Where(f => f.sex == "M").ToList();
|
||||
var femaleFollowers = request.PreBeds.Where(f => f.sex == "F").ToList();
|
||||
|
||||
void AllocateByGender(List<FollowerBedResponse> followers, bool isMale)
|
||||
{
|
||||
var rooms = roomGroups.Where(r => r.Gender == isMale).ToList();
|
||||
int remaining = followers.Count;
|
||||
int index = 0;
|
||||
|
||||
while (remaining > 0 && rooms.Any(r => r.Beds.Count > 0))
|
||||
{
|
||||
// 找能容纳所有人的房间
|
||||
var fullRoom = rooms
|
||||
.Where(r => r.Beds.Count >= remaining)
|
||||
.OrderBy(r => r.Beds.Count - remaining) // 分配后剩余床位最少优先
|
||||
.FirstOrDefault();
|
||||
|
||||
if (fullRoom != null)
|
||||
{
|
||||
// 分配所有剩余人员
|
||||
for (int i = index; i < followers.Count; i++)
|
||||
{
|
||||
var follower = followers[i];
|
||||
var bed = fullRoom.Beds.First();
|
||||
data.Add(new FollowerBedResponse
|
||||
{
|
||||
num = follower.num,
|
||||
sex = follower.sex,
|
||||
bedUuid = bed.Uuid
|
||||
});
|
||||
fullRoom.Beds.Remove(bed);
|
||||
remaining--;
|
||||
index++;
|
||||
}
|
||||
break; // 全部分配完
|
||||
}
|
||||
else
|
||||
{
|
||||
// 找剩余床位最多的房间
|
||||
var room = rooms.OrderByDescending(r => r.Beds.Count).First();
|
||||
int toAssign = Math.Min(remaining, room.Beds.Count);
|
||||
|
||||
for (int i = 0; i < toAssign; i++)
|
||||
{
|
||||
var follower = followers[index++];
|
||||
var bed = room.Beds.First();
|
||||
data.Add(new FollowerBedResponse
|
||||
{
|
||||
num = follower.num,
|
||||
sex = follower.sex,
|
||||
bedUuid = bed.Uuid
|
||||
});
|
||||
room.Beds.Remove(bed);
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(index < followers.Count)
|
||||
{
|
||||
isAllallocation = false;
|
||||
return;
|
||||
}
|
||||
// 剩余无法分配的人员
|
||||
for (int i = index; i < followers.Count; i++)
|
||||
{
|
||||
|
||||
data.Add(new FollowerBedResponse
|
||||
{
|
||||
num = followers[i].num,
|
||||
sex = followers[i].sex,
|
||||
bedUuid = null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 先分配男生,再分配女生
|
||||
AllocateByGender(maleFollowers, true);
|
||||
if (!isAllallocation)
|
||||
{
|
||||
return BadRequest("分配失敗,床位不足");
|
||||
}
|
||||
AllocateByGender(femaleFollowers, false);
|
||||
if (!isAllallocation)
|
||||
{
|
||||
return BadRequest("分配失敗,床位不足");
|
||||
}
|
||||
|
||||
return Ok(new { message = "預分配成功", data });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("api/region/bed/confirmallocation")]
|
||||
public async Task<IHttpActionResult> ConfirmAutoAllocationBed([FromBody] ConfirmAllocationRequest request)
|
||||
{
|
||||
if (request?.PreBeds == null || !request.PreBeds.Any())
|
||||
return BadRequest("请求床位不能为空");
|
||||
|
||||
using (var transaction = _db.Database.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
DateTime allocationStart = request.CheckInAt.Date; // 保證時間清零
|
||||
DateTime? allocationEnd = request.CheckOutAt?.Date;
|
||||
|
||||
foreach (var req in request.PreBeds)
|
||||
{
|
||||
if (req.bedUuid != null)
|
||||
{
|
||||
// 先拉出床位相關排程到內存,避免 EF 不支援 .Date
|
||||
var schedules = _db.RegionAndRoomAndBedSchedule
|
||||
.Where(s => s.IsDeleted == false && s.IsActive && s.TargetUuid == req.bedUuid)
|
||||
.ToList();
|
||||
|
||||
bool conflictExists = schedules.Any(s =>
|
||||
s.ScheduleDate == null // 長期占用
|
||||
|| (allocationEnd.HasValue
|
||||
? (s.ScheduleDate >= allocationStart && s.ScheduleDate <= allocationEnd.Value)
|
||||
: s.ScheduleDate >= allocationStart)
|
||||
);
|
||||
|
||||
if (conflictExists)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return BadRequest($"床位 {req.bedUuid} 在时间段内已被占用,分配失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 找到房間 UUID
|
||||
Guid? roomUuid = null;
|
||||
if (req.bedUuid != null)
|
||||
{
|
||||
var bed = _db.RegionRoomBed.Find(req.bedUuid);
|
||||
if (bed != null)
|
||||
roomUuid = bed.RoomUuid;
|
||||
}
|
||||
|
||||
// 新增挂单入住客人记录
|
||||
var guest = new GuaDanOrderGuest
|
||||
{
|
||||
GuaDanOrderNo = request.OrderNo,
|
||||
FollowerNum = req.num,
|
||||
BedUuid = req.bedUuid,
|
||||
Uuid = Guid.NewGuid(),
|
||||
RoomUuid = roomUuid,
|
||||
CheckInAt = allocationStart,
|
||||
CheckOutAt = allocationEnd,
|
||||
};
|
||||
_db.GuaDanOrderGuest.Add(guest);
|
||||
|
||||
// 新增每日排程
|
||||
if (allocationEnd.HasValue)
|
||||
{
|
||||
for (var date = allocationStart; date <= allocationEnd.Value; date = date.AddDays(1))
|
||||
{
|
||||
var newSchedule = new RegionAndRoomAndBedSchedule
|
||||
{
|
||||
Uuid = Guid.NewGuid(),
|
||||
TargetUuid = req.bedUuid,
|
||||
ScheduleDate = date,
|
||||
UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation,
|
||||
IsDeleted = false,
|
||||
IsActive = true,
|
||||
CreatedBy = "系统自动分配",
|
||||
CreatedAt = DateTime.Now,
|
||||
GuaDanOrderNo = guest.GuaDanOrderNo
|
||||
};
|
||||
_db.RegionAndRoomAndBedSchedule.Add(newSchedule);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 長期占用
|
||||
var newSchedule = new RegionAndRoomAndBedSchedule
|
||||
{
|
||||
Uuid = Guid.NewGuid(),
|
||||
TargetUuid = req.bedUuid,
|
||||
ScheduleDate = null,
|
||||
UseType = (int)RegionAndRoomAndBedSchedule.SchedulePurpose.Bed_Reservation,
|
||||
IsDeleted = false,
|
||||
IsActive = true,
|
||||
CreatedBy = "系统自动分配",
|
||||
CreatedAt = DateTime.Now,
|
||||
GuaDanOrderNo = guest.GuaDanOrderNo
|
||||
};
|
||||
_db.RegionAndRoomAndBedSchedule.Add(newSchedule);
|
||||
}
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
transaction.Commit();
|
||||
|
||||
return Ok(new { message = "分配成功" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool BedIsCanUsed(RegionRoomBed bed, DateTime? StartTime, DateTime? EndTime)
|
||||
{
|
||||
if(!bed.IsActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public class FollowerBedRequest
|
||||
{
|
||||
public int num { get; set; }
|
||||
public string sex { get; set; }
|
||||
}
|
||||
|
||||
public class FollowerBedResponse
|
||||
{
|
||||
public int num { get; set; }
|
||||
public string sex { get; set; }
|
||||
public Guid? bedUuid { get; set; } = null;
|
||||
public string bedName { get; set; }
|
||||
}
|
||||
public class ConfirmAllocationRequest
|
||||
{
|
||||
public List<FollowerBedResponse> PreBeds { get; set; }
|
||||
public string OrderNo { get; set; }
|
||||
public DateTime CheckInAt { get; set; }
|
||||
public DateTime? CheckOutAt { get; set; }
|
||||
}
|
||||
}
|
||||
67
web/App_Code/api/regionRoomBedStatusController.cs
Normal file
67
web/App_Code/api/regionRoomBedStatusController.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
|
||||
/// <summary>
|
||||
/// regionRoomBedStatusController 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class regionRoomBedStatusController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
public regionRoomBedStatusController()
|
||||
{
|
||||
//
|
||||
// TODO: 在這裡新增建構函式邏輯
|
||||
//
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("api/region/bed/status/list")]
|
||||
public IHttpActionResult getStatusList()
|
||||
{
|
||||
//返回所有状态
|
||||
var data = _db.RegionRoomBedStatus
|
||||
.Where(a => !a.IsDeleted)
|
||||
.OrderBy(a => a.Category)
|
||||
.ThenBy(a => a.Code)
|
||||
.ToList()
|
||||
.Select(s => new
|
||||
{
|
||||
s.Category,
|
||||
s.Code,
|
||||
s.Name,
|
||||
s.Description,
|
||||
s.Uuid,
|
||||
s.CategoryName
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("api/region/guadan/status/list")]
|
||||
public IHttpActionResult getGuadanStatusList()
|
||||
{
|
||||
//获取挂单状态
|
||||
var data = _db.RegionRoomBedStatus
|
||||
.Where(a => a.Category == (int)RegionRoomBedStatus.CategoryEnum.GuaDanStatus)
|
||||
.ToList();
|
||||
return Ok(data);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/bed/status/delete")]
|
||||
public IHttpActionResult DeleteBedStatus([FromUri]Guid id)
|
||||
{
|
||||
var rt = _db.RegionRoomBedStatus.Find(id);
|
||||
if (rt == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
_db.RegionRoomBedStatus.Remove(rt);
|
||||
_db.SaveChanges();
|
||||
return Ok(new { message = "刪除成功" });
|
||||
}
|
||||
}
|
||||
117
web/App_Code/api/regionRoomController.cs
Normal file
117
web/App_Code/api/regionRoomController.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using static regionController;
|
||||
|
||||
/// <summary>
|
||||
/// regionRoomController 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class regionRoomController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
public regionRoomController()
|
||||
{
|
||||
//
|
||||
// TODO: 在這裡新增建構函式邏輯
|
||||
//
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("api/region/room/get")]
|
||||
public async Task<IHttpActionResult> getRoomList()
|
||||
{
|
||||
var rooms = await _db.Room.ToListAsync();
|
||||
return Ok(rooms);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/room/create")]
|
||||
public async Task<IHttpActionResult> createRoom([FromBody] Room room)
|
||||
{
|
||||
if (!room.BedCount.HasValue)
|
||||
{
|
||||
return BadRequest("請輸入床位數量");
|
||||
}
|
||||
var newRoom = new Room();
|
||||
newRoom.Name = room.Name;
|
||||
newRoom.RegionUuid = room.RegionUuid;
|
||||
newRoom.BedCount = room.BedCount;
|
||||
newRoom.CreatedAt = room.CreatedAt;
|
||||
newRoom.Gender = room.Gender;
|
||||
newRoom.IsActive = room.IsActive;
|
||||
newRoom.Uuid = Guid.NewGuid();
|
||||
_db.Room.Add(newRoom);
|
||||
await _db.SaveChangesAsync();
|
||||
var Rooms = new
|
||||
{
|
||||
uuid = newRoom.Uuid,
|
||||
Name = newRoom.Name,
|
||||
regionUuid = newRoom.RegionUuid,
|
||||
Gender = newRoom.Gender,
|
||||
BedCount = newRoom.BedCount,
|
||||
IsActive = newRoom.IsActive,
|
||||
beds = newRoom.RegionRoomBed.Select(c => new BedDto
|
||||
{
|
||||
Uuid = c.Uuid,
|
||||
name = c.Name,
|
||||
roomUuid = c.RoomUuid,
|
||||
isactive = c.IsActive,
|
||||
statusuuid = c.StatusUuid,
|
||||
}).ToList(),
|
||||
|
||||
};
|
||||
return Ok(Rooms);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/room/update")]
|
||||
public async Task<IHttpActionResult> updateRoom([FromBody] Room room)
|
||||
{
|
||||
var oldRoom = await _db.Room.FindAsync(room.Uuid);
|
||||
if (oldRoom == null)
|
||||
{
|
||||
return BadRequest("未找到该房间信息,更新失败");
|
||||
}
|
||||
// 判斷房間中是否存在與房間性別不符的床位
|
||||
bool canProceed = !_db.RegionRoomBed
|
||||
.Any(a => a.RoomUuid == room.Uuid && a.Gender != room.Gender);
|
||||
|
||||
if (!canProceed)
|
||||
{
|
||||
// 如果有不符合性別的床位,不能繼續操作
|
||||
return BadRequest("房間中已有與房間性別不符的床位,無法操作");
|
||||
}
|
||||
if(!room.BedCount.HasValue)
|
||||
{
|
||||
return BadRequest("請輸入床位數量");
|
||||
}
|
||||
oldRoom.Name = room.Name;
|
||||
oldRoom.BedCount = room.BedCount;
|
||||
oldRoom.Gender = room.Gender;
|
||||
oldRoom.UpdatedAt = DateTime.Now;
|
||||
oldRoom.IsActive = room.IsActive;
|
||||
oldRoom.RegionUuid = room.RegionUuid;
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "更新成功"});
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/region/room/delete")]
|
||||
public async Task<IHttpActionResult> deleteRoom([FromBody] Room rm)
|
||||
{
|
||||
var room = await _db.Room.FindAsync(rm.Uuid);
|
||||
if (room == null) return BadRequest("房間不存在");
|
||||
|
||||
var beds = _db.RegionRoomBed.Where(b => b.RoomUuid == room.Uuid);
|
||||
_db.RegionRoomBed.RemoveRange(beds);
|
||||
|
||||
_db.Room.Remove(room);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "刪除成功" });
|
||||
}
|
||||
|
||||
}
|
||||
48
web/App_Code/api/regiontypeController.cs
Normal file
48
web/App_Code/api/regiontypeController.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
|
||||
/// <summary>
|
||||
/// regiontypeController 的摘要描述
|
||||
/// </summary>
|
||||
[ezAuthorize]
|
||||
public class regiontypeController: ApiController
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
public regiontypeController()
|
||||
{
|
||||
//
|
||||
// TODO: 在這裡新增建構函式邏輯
|
||||
//
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/regiontype/getreiontypelist")]
|
||||
public IHttpActionResult getRegionTypeList()
|
||||
{
|
||||
var data = _db.RegionType
|
||||
.Select(region => new
|
||||
{
|
||||
region.Uuid,
|
||||
region.Name,
|
||||
region.Code,
|
||||
isactive = region.IsActive,
|
||||
})
|
||||
.ToList();
|
||||
return Ok(data);
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("api/regiontype/delete")]
|
||||
public IHttpActionResult deleteRegionType([FromUri] Guid uuid)
|
||||
{
|
||||
var rt = _db.RegionType.Find(uuid);
|
||||
if (rt == null) {
|
||||
return NotFound();
|
||||
}
|
||||
_db.RegionType.Remove(rt);
|
||||
_db.SaveChanges();
|
||||
return Ok( new { message = "刪除成功" });
|
||||
}
|
||||
}
|
||||
20
web/App_Data/PublishProfiles/FolderProfile.pubxml
Normal file
20
web/App_Data/PublishProfiles/FolderProfile.pubxml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<DeleteExistingFiles>true</DeleteExistingFiles>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<LastUsedBuildConfiguration>Debug</LastUsedBuildConfiguration>
|
||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<PublishProvider>FileSystem</PublishProvider>
|
||||
<PublishUrl>D:\17168web</PublishUrl>
|
||||
<WebPublishMethod>FileSystem</WebPublishMethod>
|
||||
<_TargetId>Folder</_TargetId>
|
||||
<SiteUrlToLaunchAfterPublish />
|
||||
<PrecompileBeforePublish>true</PrecompileBeforePublish>
|
||||
<EnableUpdateable>true</EnableUpdateable>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<WDPMergeOption>DonotMerge</WDPMergeOption>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -74,6 +74,102 @@
|
||||
<script src="<%=ResolveUrl("~/js/moment.min.js")%>"></script>
|
||||
<script src="<%=ResolveUrl("~/js/sweetalert2/sweetalert2.all.min.js") %>"></script>
|
||||
<script src="<%=ResolveUrl("~/admin/Templates/TBS5ADM001/js/Script.js")%>"></script>
|
||||
|
||||
<script>
|
||||
//全局的VUE組件,操作提示組件
|
||||
Vue.component('message-modal', {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
title: '提示',
|
||||
message: '',
|
||||
status: 'success', // 'success' or 'error'
|
||||
callback: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
open({ title = '提示', message = '', status = 'success', callback = null }) {
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.callback = callback;
|
||||
this.show = true;
|
||||
},
|
||||
close() {
|
||||
this.show = false;
|
||||
if (this.callback) this.callback();
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="modal fade" tabindex="-1" :class="{ show: show }" style="display: block;" v-if="show" @click.self="close">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header" :class="status === 'success' ? 'bg-success text-white' : 'bg-danger text-white'">
|
||||
<h5 class="modal-title">{{ title }}</h5>
|
||||
<button type="button" class="btn-close" @click="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" :class="status === 'success' ? 'btn-success' : 'btn-danger'" @click="close">確定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
Vue.component('confirm-modal', {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
title: '確認操作',
|
||||
message: '是否確定要執行此操作?',
|
||||
confirmCallback: null,
|
||||
cancelCallback: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
open(opts) {
|
||||
this.title = opts.title || '確認操作';
|
||||
this.message = opts.message || '是否確定要執行此操作?';
|
||||
this.confirmCallback = opts.onConfirm || null;
|
||||
this.cancelCallback = opts.onCancel || null;
|
||||
this.show = true;
|
||||
},
|
||||
confirm() {
|
||||
this.show = false;
|
||||
if (this.confirmCallback) this.confirmCallback();
|
||||
},
|
||||
cancel() {
|
||||
this.show = false;
|
||||
if (this.cancelCallback) this.cancelCallback();
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="modal fade" tabindex="-1"
|
||||
:class="{ show: show }"
|
||||
style="display:block;" v-if="show"
|
||||
@click.self="cancel">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-warning text-white">
|
||||
<h5 class="modal-title">{{ title }}</h5>
|
||||
<button type="button" class="btn-close" @click="cancel"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" @click="cancel">取消</button>
|
||||
<button type="button" class="btn btn-danger" @click="confirm">確認</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
});
|
||||
</script>
|
||||
|
||||
<asp:ContentPlaceHolder id="footer_script" runat="server">
|
||||
|
||||
</asp:ContentPlaceHolder>
|
||||
|
||||
1548
web/admin/guadan/create.aspx
Normal file
1548
web/admin/guadan/create.aspx
Normal file
File diff suppressed because it is too large
Load Diff
14
web/admin/guadan/create.aspx.cs
Normal file
14
web/admin/guadan/create.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_guadan_create : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
52
web/admin/guadan/guadantime/edit.aspx
Normal file
52
web/admin/guadan/guadantime/edit.aspx
Normal file
@@ -0,0 +1,52 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="edit.aspx.cs" Inherits="admin_guadan_guadantime_edit" %>
|
||||
|
||||
<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">
|
||||
<div class="container mt-4">
|
||||
<h3 class="mb-4">编辑挂单时间设置</h3>
|
||||
|
||||
<asp:Panel CssClass="card p-4 shadow-sm">
|
||||
<!-- 最早入住时间 -->
|
||||
<div class="mb-3 row align-items-center">
|
||||
<label class="col-sm-3 col-form-label">最早入住时间:</label>
|
||||
<div class="col-sm-3">
|
||||
<asp:DropDownList ID="ddlEarliestCheckIn" runat="server" CssClass="form-select"></asp:DropDownList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最晚退房时间 -->
|
||||
<div class="mb-3 row align-items-center">
|
||||
<label class="col-sm-3 col-form-label">最晚退房时间:</label>
|
||||
<div class="col-sm-3">
|
||||
<asp:DropDownList ID="ddlLatestCheckOut" runat="server" CssClass="form-select"></asp:DropDownList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 是否可用 -->
|
||||
<div class="mb-3 row align-items-center">
|
||||
<label class="col-sm-3 col-form-label">是否可用:</label>
|
||||
<div class="col-sm-3">
|
||||
<asp:CheckBox ID="chkIsActive" runat="server" CssClass="form-check-input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<div class="mt-4">
|
||||
<asp:Button ID="btnSave" runat="server" Text="保存" CssClass="btn btn-primary me-2" OnClick="btnSave_Click" />
|
||||
<asp:HyperLink ID="hlBack" runat="server" NavigateUrl="timeindex.aspx" Text="返回列表" CssClass="btn btn-secondary" />
|
||||
</div>
|
||||
|
||||
<!-- 消息提示 -->
|
||||
<div id="divMessage" runat="server" class="mt-3 text-success"></div>
|
||||
</asp:Panel>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
</asp:Content>
|
||||
|
||||
100
web/admin/guadan/guadantime/edit.aspx.cs
Normal file
100
web/admin/guadan/guadantime/edit.aspx.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_guadan_guadantime_edit : MyWeb.config
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
private Guid SettingId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Guid.TryParse(Request.QueryString["id"], out Guid id))
|
||||
return id;
|
||||
else
|
||||
return Guid.Empty;
|
||||
}
|
||||
}
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
BindTimeDropdowns();
|
||||
|
||||
if (SettingId != Guid.Empty)
|
||||
{
|
||||
LoadSetting();
|
||||
}
|
||||
else
|
||||
{
|
||||
divMessage.InnerText = "未指定要编辑的记录。";
|
||||
btnSave.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
private void BindTimeDropdowns()
|
||||
{
|
||||
ddlEarliestCheckIn.Items.Clear();
|
||||
ddlLatestCheckOut.Items.Clear();
|
||||
|
||||
for (int h = 0; h < 24; h++)
|
||||
{
|
||||
ddlEarliestCheckIn.Items.Add($"{h:D2}:00");
|
||||
ddlEarliestCheckIn.Items.Add($"{h:D2}:30");
|
||||
|
||||
ddlLatestCheckOut.Items.Add($"{h:D2}:00");
|
||||
ddlLatestCheckOut.Items.Add($"{h:D2}:30");
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadSetting()
|
||||
{
|
||||
var setting = _db.GuadanTimeSetting.FirstOrDefault(x => x.Id == SettingId);
|
||||
if (setting != null)
|
||||
{
|
||||
ddlEarliestCheckIn.SelectedValue = setting.EarliestCheckIn;
|
||||
ddlLatestCheckOut.SelectedValue = setting.LatestCheckOut;
|
||||
chkIsActive.Checked = setting.IsActive;
|
||||
}
|
||||
else
|
||||
{
|
||||
divMessage.InnerText = "找不到指定记录。";
|
||||
btnSave.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected void btnSave_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (SettingId == Guid.Empty)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
var setting = _db.GuadanTimeSetting.FirstOrDefault(x => x.Id == SettingId);
|
||||
if (setting != null)
|
||||
{
|
||||
setting.EarliestCheckIn = ddlEarliestCheckIn.SelectedValue;
|
||||
setting.LatestCheckOut = ddlLatestCheckOut.SelectedValue;
|
||||
setting.IsActive = chkIsActive.Checked;
|
||||
setting.UpdatedAt = DateTime.Now;
|
||||
|
||||
_db.SaveChanges();
|
||||
|
||||
divMessage.InnerText = "保存成功!";
|
||||
}
|
||||
else
|
||||
{
|
||||
divMessage.InnerText = "记录不存在,保存失败。";
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
divMessage.InnerText = "保存失败:" + ex.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
web/admin/guadan/guadantime/timeindex.aspx
Normal file
40
web/admin/guadan/guadantime/timeindex.aspx
Normal file
@@ -0,0 +1,40 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="timeindex.aspx.cs" Inherits="admin_guadan_guadantime_timeindex" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<nav>
|
||||
<a href="timeset.aspx" class="btn btn-primary">
|
||||
新建时间参数
|
||||
</a>
|
||||
</nav>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
<h3>挂单时间设置列表</h3>
|
||||
<div id="divMessage" runat="server" style="color:red; margin-top:10px;"></div>
|
||||
|
||||
<asp:GridView ID="gvTimeSettings" runat="server" AutoGenerateColumns="False"
|
||||
CssClass="table table-bordered" OnRowCommand="gvTimeSettings_RowCommand">
|
||||
<Columns>
|
||||
<asp:BoundField DataField="EarliestCheckIn" HeaderText="最早入住时间" />
|
||||
<asp:BoundField DataField="LatestCheckOut" HeaderText="最晚退房时间" />
|
||||
<asp:BoundField DataField="IsActive" HeaderText="是否可用" />
|
||||
<asp:BoundField DataField="CreatedAt" HeaderText="创建时间" DataFormatString="{0:yyyy-MM-dd HH:mm}" />
|
||||
<asp:TemplateField HeaderText="操作">
|
||||
<ItemTemplate>
|
||||
<asp:HyperLink ID="hlEdit" runat="server" Text="编辑"
|
||||
NavigateUrl='<%# "edit.aspx?id=" + Eval("Id") %>' CssClass="btn btn-sm btn-primary"></asp:HyperLink>
|
||||
<asp:Button ID="btnDelete" runat="server" Text="删除" CommandName="DeleteItem"
|
||||
CommandArgument='<%# Eval("Id") %>' CssClass="btn btn-sm btn-danger"
|
||||
OnClientClick="return confirm('确定删除这条记录吗?');" />
|
||||
</ItemTemplate>
|
||||
</asp:TemplateField>
|
||||
</Columns>
|
||||
</asp:GridView>
|
||||
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
</asp:Content>
|
||||
|
||||
59
web/admin/guadan/guadantime/timeindex.aspx.cs
Normal file
59
web/admin/guadan/guadantime/timeindex.aspx.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_guadan_guadantime_timeindex : MyWeb.config
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
BindTimeSettings();
|
||||
}
|
||||
}
|
||||
private void BindTimeSettings()
|
||||
{
|
||||
{
|
||||
// 取得最近所有挂单时间设置
|
||||
var list = _db.GuadanTimeSetting
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
.ToList();
|
||||
|
||||
gvTimeSettings.DataSource = list;
|
||||
gvTimeSettings.DataBind();
|
||||
}
|
||||
}
|
||||
protected void gvTimeSettings_RowCommand(object sender, GridViewCommandEventArgs e)
|
||||
{
|
||||
if (e.CommandName == "DeleteItem")
|
||||
{
|
||||
Guid id = Guid.Parse(e.CommandArgument.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
var setting = _db.GuadanTimeSetting.FirstOrDefault(x => x.Id == id);
|
||||
if (setting != null)
|
||||
{
|
||||
_db.GuadanTimeSetting.Remove(setting);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// 重新绑定列表
|
||||
BindTimeSettings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 显示错误信息
|
||||
divMessage.InnerText = "删除失败:" + ex.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
36
web/admin/guadan/guadantime/timeset.aspx
Normal file
36
web/admin/guadan/guadantime/timeset.aspx
Normal file
@@ -0,0 +1,36 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="timeset.aspx.cs" Inherits="admin_guadan_guadantime_timeset" %>
|
||||
|
||||
<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">
|
||||
<div>
|
||||
<h3>入住时间設定</h3>
|
||||
|
||||
<div>
|
||||
<label>最早入住時間:</label>
|
||||
<asp:DropDownList ID="ddlEarliestCheckIn" runat="server"></asp:DropDownList>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>最晚退房時間:</label>
|
||||
<asp:DropDownList ID="ddlLatestCheckOut" runat="server"></asp:DropDownList>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:10px;">
|
||||
<asp:Button ID="btnSave" runat="server" Text="保存" OnClick="btnSave_Click" />
|
||||
</div>
|
||||
|
||||
<div id="divMessage" runat="server" style="color:green; margin-top:10px;"></div>
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
|
||||
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
|
||||
</asp:Content>
|
||||
|
||||
65
web/admin/guadan/guadantime/timeset.aspx.cs
Normal file
65
web/admin/guadan/guadantime/timeset.aspx.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_guadan_guadantime_timeset : MyWeb.config
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
// 預設時間
|
||||
// 初始化半小时间隔
|
||||
for (int h = 0; h < 24; h++)
|
||||
{
|
||||
ddlEarliestCheckIn.Items.Add(new System.Web.UI.WebControls.ListItem($"{h:D2}:00"));
|
||||
ddlEarliestCheckIn.Items.Add(new System.Web.UI.WebControls.ListItem($"{h:D2}:30"));
|
||||
|
||||
ddlLatestCheckOut.Items.Add(new System.Web.UI.WebControls.ListItem($"{h:D2}:00"));
|
||||
ddlLatestCheckOut.Items.Add(new System.Web.UI.WebControls.ListItem($"{h:D2}:30"));
|
||||
}
|
||||
|
||||
// 預設值
|
||||
ddlEarliestCheckIn.SelectedValue = "08:00";
|
||||
ddlLatestCheckOut.SelectedValue = "14:00";
|
||||
}
|
||||
}
|
||||
|
||||
protected void btnSave_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(_db.GuadanTimeSetting.Where( a => a.IsActive == true).Count() > 0)
|
||||
{
|
||||
divMessage.InnerText = "已经存在有效的时间设置";
|
||||
return;
|
||||
}
|
||||
string earliest = ddlEarliestCheckIn.SelectedValue;
|
||||
string latest = ddlLatestCheckOut.SelectedValue;
|
||||
|
||||
var setting = new GuadanTimeSetting
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
EarliestCheckIn = earliest,
|
||||
LatestCheckOut = latest,
|
||||
IsActive = true,
|
||||
CreatedAt = DateTime.Now
|
||||
};
|
||||
|
||||
_db.GuadanTimeSetting.Add(setting);
|
||||
_db.SaveChanges();
|
||||
// TODO: 保存到資料庫
|
||||
divMessage.InnerText = $"保存成功!最早入住:{earliest}, 最晚退房:{latest}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
divMessage.InnerText = "保存失败:" + ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
112
web/admin/guadan/index.aspx
Normal file
112
web/admin/guadan/index.aspx
Normal file
@@ -0,0 +1,112 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="admin_guadan_index" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<nav>
|
||||
<a href="create.aspx" class="btn btn-primary" >新建掛單</a>
|
||||
</nav>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
|
||||
<div class="mx-5">
|
||||
<v-data-table
|
||||
:items="items"
|
||||
:headers="headers">
|
||||
<template #item.actions="{item}">
|
||||
<a :href="'create.aspx?orderId='+item.guaDanOrderNo" class="btn btn-secondary">編輯</a>
|
||||
<a class="btn btn-outline-danger" @click="deleteGuadanOrder(item)">取消</a>
|
||||
</template>
|
||||
<template #item.room="{item}">
|
||||
{{item.room.name}}
|
||||
</template>
|
||||
<template #item.bed="{item}">
|
||||
{{item.bed.name}}
|
||||
</template>
|
||||
<template #item.status="{item}">
|
||||
{{item.status}}
|
||||
</template>
|
||||
<template #item.start_date="{item}">
|
||||
{{item.start_date | timeString('YYYY/MM/DD HH:mm')}}
|
||||
</template>
|
||||
<template #item.end_date="{item}">
|
||||
{{item.end_date | timeString('YYYY/MM/DD HH:mm')}}
|
||||
</template>
|
||||
<template #item.created_at="{item}">
|
||||
{{item.created_at | timeString('YYYY/MM/DD HH:mm')}}
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
<!-- 更新修改確認彈出視窗 -->
|
||||
<message-modal ref="messageModal"></message-modal>
|
||||
<!-- 刪除確認彈出視窗 -->
|
||||
<confirm-modal ref="confirmModal"></confirm-modal>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
<script>
|
||||
Vue.filter('timeString', function (value, myFormat) {
|
||||
return value == null || value == "" ? "" : moment(value).format(myFormat || 'YYYY-MM-DD, HH:mm:ss');
|
||||
});
|
||||
new Vue({
|
||||
el: '#app',
|
||||
vuetify: new Vuetify(vuetify_options),
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
headers: [
|
||||
{ text: '登记挂单莲友', value: 'bookerName'},
|
||||
{ text: '起始日期', value: 'start_date', align: 'center' },
|
||||
{ text: '結束日期', value: 'end_date', align: 'center' },
|
||||
{ text: '掛單人數', value: 'guest_count' },
|
||||
{ text: '狀態', value: 'statusName', align: 'center' },
|
||||
{ text: '建立時間', value: 'created_at', align: 'center' },
|
||||
{ text: '備註', value: 'notes', align: 'center' },
|
||||
{ text: '操作', value: 'actions', align: 'center' }
|
||||
],
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getGuadanOrder() {
|
||||
axios.get('/api/guadan/list')
|
||||
.then((res) => {
|
||||
this.items = res.data;
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
})
|
||||
},
|
||||
deleteGuadanOrder(order) {
|
||||
this.$refs.confirmModal.open({
|
||||
message: '確認取消掛單?',
|
||||
onConfirm: () => {
|
||||
axios.post('/api/guadan/delete', null, {
|
||||
params: {
|
||||
uuid: order.uuid
|
||||
}
|
||||
}).then((res) => {
|
||||
this.items = this.items.filter(a => a.uuid != order.uuid)
|
||||
this.$refs.messageModal.open({
|
||||
message: '取消成功'
|
||||
})
|
||||
}).catch((error) => {
|
||||
this.$refs.messageModal.open({
|
||||
message: '取消失敗'
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.getGuadanOrder();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</asp:Content>
|
||||
|
||||
14
web/admin/guadan/index.aspx.cs
Normal file
14
web/admin/guadan/index.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_guadan_index : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
143
web/admin/guadan/statistics.aspx
Normal file
143
web/admin/guadan/statistics.aspx
Normal file
@@ -0,0 +1,143 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="statistics.aspx.cs" Inherits="admin_guadan_statistics" %>
|
||||
|
||||
<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">
|
||||
<div class="container my-4">
|
||||
<!-- 客房统计 -->
|
||||
<div class="row row-cols-2 row-cols-sm-3 row-cols-md-4 row-cols-lg-5 g-3">
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">🏠</div>
|
||||
<div class="text-muted small mt-1">总房间数量</div>
|
||||
<div class="fw-bold fs-5 mt-1">{{ roomStatistics.roomCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">🚪</div>
|
||||
<div class="text-muted small mt-1">空房间数量</div>
|
||||
<div class="fw-bold fs-5 mt-1">{{ roomStatistics.emptyRoomCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">🛏️</div>
|
||||
<div class="text-muted small mt-1">总床位数量</div>
|
||||
<div class="fw-bold fs-5 mt-1">
|
||||
{{ roomStatistics.bedCount }} (男:{{ roomStatistics.maleBedCount }},女:{{ roomStatistics.femaleBedCount }})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">🛌</div>
|
||||
<div class="text-muted small mt-1">可用空床</div>
|
||||
<div class="fw-bold fs-5 mt-1">
|
||||
{{ roomStatistics.emptyBedCount }} (男:{{ roomStatistics.emptyMaleBedCount }},女:{{ roomStatistics.emptyFemaleBedCount }})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 挂单统计 -->
|
||||
<div class="row row-cols-2 row-cols-sm-3 row-cols-md-4 row-cols-lg-5 g-3 mt-1">
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">📝</div>
|
||||
<div class="text-muted small mt-1">总挂单次数</div>
|
||||
<div class="fw-bold fs-5 mt-1">{{ guadanStatistics.guadanTotalCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">📋</div>
|
||||
<div class="text-muted small mt-1">当前挂单数量</div>
|
||||
<div class="fw-bold fs-5 mt-1">{{ guadanStatistics.guadanCurrentCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">👥</div>
|
||||
<div class="text-muted small mt-1">总挂单人数</div>
|
||||
<div class="fw-bold fs-5 mt-1">
|
||||
{{ guadanStatistics.guadanPeopleTotal }} (男:{{ guadanStatistics.guadanPeopleMale }},女:{{ guadanStatistics.guadanPeopleFemale }})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="p-3 bg-light text-center rounded shadow" style="min-height: 180px;">
|
||||
<div class="fs-2">👨👩</div>
|
||||
<div class="text-muted small mt-1">当前挂单人数</div>
|
||||
<div class="fw-bold fs-5 mt-1">
|
||||
{{ guadanStatistics.guadanPeopleCurrent }} (男:{{ guadanStatistics.guadanPeopleCurrentMale }},女:{{ guadanStatistics.guadanPeopleCurrentFemale }})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
vuetify: new Vuetify(vuetify_options),
|
||||
data() {
|
||||
return {
|
||||
roomStatistics: {
|
||||
roomCount: 0,
|
||||
emptyRoomCount: 0,
|
||||
bedCount: 0,
|
||||
maleBedCount: 0,
|
||||
femaleBedCount: 0,
|
||||
emptyBedCount: 0,
|
||||
emptyMaleBedCount: 0,
|
||||
emptyFemaleBedCount: 0
|
||||
},
|
||||
guadanStatistics: {
|
||||
guadanTotalCount: 0,
|
||||
guadanCurrentCount: 0,
|
||||
guadanPeopleTotal: 0,
|
||||
guadanPeopleMale: 0,
|
||||
guadanPeopleFemale: 0,
|
||||
guadanPeopleCurrent: 0,
|
||||
guadanPeopleCurrentMale: 0,
|
||||
guadanPeopleCurrentFemale: 0
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
GetGuadanStatistics() {
|
||||
axios.get('/api/guadanStatistics/GetGuadanStatistics')
|
||||
.then((res) => {
|
||||
this.roomStatistics = res.data.roomStatistics;
|
||||
this.guadanStatistics = res.data.guadanStatistics;
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.GetGuadanStatistics();
|
||||
|
||||
// 每两分钟更新一次 (2 * 60 * 1000 毫秒)
|
||||
setInterval(() => {
|
||||
this.GetGuadanStatistics();
|
||||
}, 1 * 60 * 1000);
|
||||
},
|
||||
})
|
||||
</script>
|
||||
</asp:Content>
|
||||
|
||||
14
web/admin/guadan/statistics.aspx.cs
Normal file
14
web/admin/guadan/statistics.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_guadan_statistics : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
13
web/admin/guadan/update.aspx
Normal file
13
web/admin/guadan/update.aspx
Normal file
@@ -0,0 +1,13 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="update.aspx.cs" Inherits="admin_guadan_update" %>
|
||||
|
||||
<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">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
</asp:Content>
|
||||
|
||||
14
web/admin/guadan/update.aspx.cs
Normal file
14
web/admin/guadan/update.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_guadan_update : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
48
web/admin/region/bed/bedstatus/create.aspx
Normal file
48
web/admin/region/bed/bedstatus/create.aspx
Normal file
@@ -0,0 +1,48 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="create.aspx.cs" Inherits="admin_region_bed_bedstatus_create" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<div></div>
|
||||
<div>
|
||||
<a href="index.aspx" class="btn btn-primary">回列表</a>
|
||||
</div>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
<div class="container">
|
||||
<div class="card shadow-sm my-3">
|
||||
<div class="card-header">
|
||||
<asp:Literal ID="L_title" runat="server" Text="新增床位状态" />
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<asp:Literal ID="L_msg" runat="server" />
|
||||
<asp:HiddenField ID="HF_Id" runat="server" />
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="TB_Name">名稱</label>
|
||||
<asp:TextBox ID="TB_Name" runat="server" CssClass="form-control" />
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="TB_Code">代碼</label>
|
||||
<asp:TextBox ID="TB_Code" runat="server" CssClass="form-control" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="TB_Category">状态所属分类(挂单,房间,床位)</label>
|
||||
<asp:DropDownList ID="TB_Category" runat="server" CssClass="form-control"></asp:DropDownList>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="Description">說明</label>
|
||||
<asp:TextBox ID="Description" runat="server" CssClass="form-control" TextMode="MultiLine" Rows="3" />
|
||||
</div>
|
||||
|
||||
<asp:Button ID="BTN_Save" runat="server" Text="儲存" CssClass="btn btn-primary" OnClick="BTN_Save_Click" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
</asp:Content>
|
||||
|
||||
103
web/admin/region/bed/bedstatus/create.aspx.cs
Normal file
103
web/admin/region/bed/bedstatus/create.aspx.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_region_bed_bedstatus_create : MyWeb.config
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack) // 加這行
|
||||
{
|
||||
if (Guid.TryParse(Request.QueryString["statusid"], out Guid id))
|
||||
{
|
||||
LoadData(id);
|
||||
L_title.Text = "編輯區域類型";
|
||||
}
|
||||
var categoryList = RegionRoomBedStatus.GetCategoryList();
|
||||
// 假设你的下拉控件ID叫 ddlCategory
|
||||
TB_Category.DataSource = categoryList;
|
||||
TB_Category.DataTextField = "Text"; // 显示文字
|
||||
TB_Category.DataValueField = "Value"; // 选项值
|
||||
TB_Category.DataBind();
|
||||
TB_Category.Items.Insert(0, new ListItem("--请选择分类--", ""));
|
||||
}
|
||||
}
|
||||
private void LoadData(Guid id)
|
||||
{
|
||||
var rt = _db.RegionRoomBedStatus.FirstOrDefault(r => r.Uuid == id);
|
||||
if (rt == null)
|
||||
{
|
||||
L_msg.Text = "<div class='alert alert-danger'>找不到資料</div>";
|
||||
BTN_Save.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
HF_Id.Value = rt.Uuid.ToString();
|
||||
TB_Name.Text = rt.Name;
|
||||
TB_Code.Text = rt.Code;
|
||||
TB_Category.SelectedValue = rt.Category?.ToString();
|
||||
Description.Text = rt.Description;
|
||||
|
||||
}
|
||||
protected void BTN_Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
RegionRoomBedStatus rt;
|
||||
if (Guid.TryParse(HF_Id.Value, out Guid id))
|
||||
{
|
||||
// 更新
|
||||
rt = _db.RegionRoomBedStatus.FirstOrDefault(r => r.Uuid == id);
|
||||
if (rt == null)
|
||||
{
|
||||
L_msg.Text = "<div class='alert alert-danger'>資料不存在</div>";
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 新增
|
||||
rt = new RegionRoomBedStatus();
|
||||
rt.Uuid = Guid.NewGuid();
|
||||
_db.RegionRoomBedStatus.Add(rt);
|
||||
}
|
||||
|
||||
rt.Name = TB_Name.Text.Trim();
|
||||
if (rt.Name.Length == 0)
|
||||
{
|
||||
L_msg.Text = "<div class='alert alert-danger'>名稱不能为空</div>";
|
||||
return;
|
||||
}
|
||||
rt.Code = TB_Code.Text.Trim();
|
||||
rt.Description = Description.Text.Trim();
|
||||
if(int.TryParse(TB_Category.SelectedValue, out int category))
|
||||
{
|
||||
rt.Category = category;
|
||||
}
|
||||
else
|
||||
{
|
||||
rt.Category = null;
|
||||
}
|
||||
_db.SaveChanges();
|
||||
|
||||
L_msg.Text = "<div class='alert alert-success'>儲存成功</div>";
|
||||
|
||||
// 如果是新增,更新隱藏欄位並切換標題為編輯
|
||||
if (HF_Id.Value == "")
|
||||
{
|
||||
HF_Id.Value = rt.Uuid.ToString();
|
||||
L_title.Text = "編輯區域類型";
|
||||
}
|
||||
Response.Redirect("index.aspx");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
L_msg.Text = $"<div class='alert alert-danger'>錯誤:{ex.Message}</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
99
web/admin/region/bed/bedstatus/index.aspx
Normal file
99
web/admin/region/bed/bedstatus/index.aspx
Normal file
@@ -0,0 +1,99 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="admin_region_bed_bedstatus_index" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<a href="create.aspx" class="btn btn-primary">
|
||||
<i class="mdi mdi-plus"></i>新增床位狀態
|
||||
</a>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
<div class="container">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">床位和掛單狀態列表</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="items"
|
||||
:loading="loading"
|
||||
>
|
||||
<template #item.actions="{item}">
|
||||
<a :href="'create.aspx?statusid='+item.uuid" class="btn btn-primary"><i class="mdi mdi-pencil"></i>修改</a>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-danger"
|
||||
@click="confirmDeleteStatus(item)"
|
||||
>
|
||||
<i class="mdi mdi-delete"></i> 刪除
|
||||
</button>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<message-modal ref="messageModal"></message-modal>
|
||||
<confirm-modal ref="confirmModal"></confirm-modal>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
<script>
|
||||
new Vue({
|
||||
'el': '#app',
|
||||
vuetify: new Vuetify(vuetify_options),
|
||||
data() {
|
||||
return {
|
||||
headers: [
|
||||
{ text: 'Id', value: 'id' },
|
||||
{ text: '狀態名稱', value: 'name' },
|
||||
{ text: '狀態代碼', value: 'code' },
|
||||
{ text: '描述', value: 'description' },
|
||||
{ text: '状态分类', value: 'categoryName'},
|
||||
{ text: '', value: 'actions' }
|
||||
],
|
||||
items: [],
|
||||
loading: true, // 初始時顯示動畫
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getStatusList() {
|
||||
axios.get('/api/region/bed/status/list')
|
||||
.then((res) => {
|
||||
this.items = res.data
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
confirmDeleteStatus(item) {
|
||||
this.$refs.confirmModal.open({
|
||||
message: '是否確認刪除給床位狀態',
|
||||
onConfirm: () => {
|
||||
this.deleteStatus(item);
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteStatus(item) {
|
||||
axios.post('/api/region/bed/status/delete', null, {
|
||||
params: { id: item.uuid }
|
||||
})
|
||||
.then(() => {
|
||||
this.items = this.items.filter(i => i.uuid != item.uuid);
|
||||
this.$refs.messageModal.open({
|
||||
title: '操作成功',
|
||||
message: '刪除成功!',
|
||||
status: 'success'
|
||||
});
|
||||
})
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.getStatusList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</asp:Content>
|
||||
|
||||
14
web/admin/region/bed/bedstatus/index.aspx.cs
Normal file
14
web/admin/region/bed/bedstatus/index.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_region_bed_bedstatus_index : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
391
web/admin/region/bed/index.aspx
Normal file
391
web/admin/region/bed/index.aspx
Normal file
@@ -0,0 +1,391 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="admin_region_bed_index" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<div class="card mb-3 w-100">
|
||||
<div class="card-body bg-light p-3 rounded" style="height: 150px;">
|
||||
<div class="d-flex h-100 w-100">
|
||||
|
||||
<!-- 左側統計區塊 40% -->
|
||||
<div class="d-flex gap-2 pe-3" style="flex: 0 0 40%; height: 100%; border-right: 2px solid #ccc;">
|
||||
<!-- 總房間 -->
|
||||
<div class="p-2 border rounded text-center flex-fill d-flex flex-column justify-content-center" style="background-color: #f0f8ff;">
|
||||
<div><strong>總房間</strong></div>
|
||||
<div>{{ summary?.totalRooms || 0 }}</div>
|
||||
<div class="text-primary">男: {{ summary?.totalMaleRooms || 0 }}</div>
|
||||
<div class="text-danger">女: {{ (summary?.totalRooms || 0) - (summary?.totalMaleRooms || 0) }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 總床位 -->
|
||||
<div class="p-2 border rounded text-center flex-fill d-flex flex-column justify-content-center" style="background-color: #fff0f5;">
|
||||
<div><strong>總床位</strong></div>
|
||||
<div>{{ summary?.totalBeds || 0 }}</div>
|
||||
<div class="text-primary">男: {{ summary?.totalMaleBeds || 0 }}</div>
|
||||
<div class="text-danger">女: {{ (summary?.totalBeds || 0) - (summary?.totalMaleBeds || 0) }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 可用床位 -->
|
||||
<div class="p-2 border rounded text-center flex-fill d-flex flex-column justify-content-center" style="background-color: #f5fff0;">
|
||||
<!-- 標題 -->
|
||||
<h6 class="mb-2">查詢結果</h6>
|
||||
<!-- 內容 -->
|
||||
<div><strong>可用床位</strong></div>
|
||||
<div class="text-primary">男: {{ filteredStats.maleBeds }}|女: {{ filteredStats.femaleBeds }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右側篩選區塊 60% -->
|
||||
<div class="d-flex gap-3 ms-10" style="flex: 0 0 60%; height: 100%; align-items: flex-start;">
|
||||
|
||||
<!-- 分組 1: 區域 & 房間下拉框 -->
|
||||
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 160px;">
|
||||
<h6 class="mb-2">區域與房間</h6>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<select class="form-select" v-model="filter.selectedArea">
|
||||
<option :value=null>全部區域</option>
|
||||
<option v-for="region in filter.areas" :value="region.uuid" :key="region.uuid">{{region.name}}</option>
|
||||
</select>
|
||||
<select class="form-select" v-model="filter.selectedRoom">
|
||||
<option :value=null>房間</option>
|
||||
<option v-for="room in filter.rooms" :value="room.uuid" :key="room.uuid">{{room.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分組 2: 日期篩選 -->
|
||||
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 160px;">
|
||||
<h6 class="mb-2">日期篩選</h6>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<input type="date" class="form-control" v-model="filter.startDate" :min="today" @change="onStartDateChange"/>
|
||||
<input type="date" class="form-control" v-model="filter.endDate" :min="filter.startDate || today"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分組 3: 占用床位 & 性別復選框 -->
|
||||
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 200px;">
|
||||
<h6 class="mb-2">床位與性別</h6>
|
||||
<!-- 上排 -->
|
||||
<div class="d-flex gap-3 p-2 mb-2 border rounded shadow-sm" style="background-color: #f8f9fa;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="occupied" v-model="filter.occupied" @change="toggleOccupied('occupied')">
|
||||
<label class="form-check-label" for="occupied">已佔用床位</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="unoccupied" v-model="filter.unoccupied" @change="toggleOccupied('unoccupied')">
|
||||
<label class="form-check-label" for="unoccupied">可用床位</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下排 -->
|
||||
<div class="d-flex gap-3 p-2 border rounded shadow-sm" style="background-color: #f8f9fa;">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="male" :checked="filter.Gender === true" @change="toggleGender(true)">
|
||||
<label class="form-check-label" for="male">男</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="female" :checked="filter.Gender === false" @change="toggleGender(false)">
|
||||
<label class="form-check-label" for="female">女</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分組 4: 查詢與清空按鈕 -->
|
||||
<div class="p-2 rounded shadow-sm" style="background-color: #fff; min-width: 120px;">
|
||||
<h6 class="mb-2">操作</h6>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<button class="btn btn-success" type="button" @click="search">查詢</button>
|
||||
<button class="btn btn-primary" type="button" @click="clearFilter">清空條件</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
<div v-for="region in regions" :key="region.uuid" class="region-block mb-4">
|
||||
<h2 class="region-title mb-3">{{ region.regionPath }}</h2>
|
||||
|
||||
<div class="row g-3 justify-content-start">
|
||||
<div v-for="room in region.room" :key="room.ruid" class="col-12 col-md-6 col-lg-4">
|
||||
|
||||
<div class="card h-100 shadow-sm" style="min-height: 300px; max-height: 400px; overflow-y: auto; border-radius: 0.5rem;">
|
||||
<!-- 上部:房間名稱 -->
|
||||
<div class="card-header bg-primary text-white fw-bold">
|
||||
<h5 class="mb-0">{{ room.name + ' (' + (room.gender ? '男' : '女') + ')' }}</h5>
|
||||
</div>
|
||||
|
||||
<!-- 下部:左右兩列 -->
|
||||
<div class="card-body p-2">
|
||||
<div class="row">
|
||||
<!-- 左列:房間資訊 -->
|
||||
<div class="col-3 border-end pe-3">
|
||||
<p class="mb-1"><strong>總床位:</strong>{{ room.stats.bedCount }}</p>
|
||||
<p class="text-success mb-1"><strong>可用:</strong>{{ room.stats.availableCount }}</p>
|
||||
<p class="text-danger mb-0"><strong>已占用:</strong>{{ room.stats.bedCount - room.stats.availableCount }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 右列:床位清單 -->
|
||||
<div class="col-9 ps-3">
|
||||
<h6 class="fw-bold mb-2">床位清單</h6>
|
||||
<div style="max-height: 200px; overflow-y: auto;">
|
||||
<table class="table table-sm table-bordered mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">床位名稱</th>
|
||||
<th scope="col">是否可用</th>
|
||||
<th scope="col">使用明細</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="bed in room.beds" :key="bed.uuid"
|
||||
:class="bed.canuse ? 'table-success' : 'table-danger'">
|
||||
<td>{{ bed.name }}</td>
|
||||
<td :class="!bed.canuse ? 'text-danger' : 'text-success'">
|
||||
{{ bed.canuse ? '是' : '否' }}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-primary" @click="showBedSchedule(bed)">查看明細</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<v-dialog v-model="bedSchedule.dialogVisible" max-width="900">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="text-h6">床位排程明細 - {{ bedSchedule.selectedBed?.name }}</span>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="closeBedSchedule">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-data-table
|
||||
:headers="bedSchedule.scheduleHeaders"
|
||||
:items="bedSchedule.selectedBed?.schedules || []"
|
||||
class="elevation-1"
|
||||
dense
|
||||
hide-default-footer
|
||||
:items-per-page="5"
|
||||
>
|
||||
<template #item.scheduleDate="{item}">
|
||||
{{item.scheduledate|timeString('YYYY-MM-DD')}}
|
||||
</template>
|
||||
<template #item.actions =" {item}">
|
||||
<a :href="'/admin/guadan/create.aspx?orderId='+item.guaDanOrderNo" class="btn btn-primary">查看掛單</a>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text color="primary" @click="closeBedSchedule">關閉</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
<style>
|
||||
.region-block { background:#f8f9fa; padding:15px; margin-bottom:20px; border-radius:8px; }
|
||||
.region-title { font-size:16px; font-weight:bold; margin-bottom:10px; }
|
||||
.room-list { display:grid; grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); gap:10px; }
|
||||
.room-card { background:#fff; border:1px solid #ddd; padding:10px; border-radius:6px; }
|
||||
.text-green { color: green; }
|
||||
.text-red { color: red; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
Vue.filter('timeString', function (value, myFormat) {
|
||||
return value == null || value == "" ? "" : moment(value).format(myFormat || 'YYYY-MM-DD, HH:mm:ss');
|
||||
});
|
||||
new Vue({
|
||||
el: '#app',
|
||||
vuetify: new Vuetify(vuetify_options),
|
||||
data: {
|
||||
regions: [],
|
||||
summary: {},
|
||||
todayStr: new Date().toISOString().split('T')[0],
|
||||
today: new Date().toISOString().split('T')[0],
|
||||
filter: {
|
||||
// 日期篩選
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
Gender: null,
|
||||
// 區域與房間選擇
|
||||
selectedArea: null,
|
||||
selectedRoom: null,
|
||||
// 佔用床位狀態(互斥)
|
||||
occupied: false, // 已佔用
|
||||
unoccupied: false, // 未佔用
|
||||
|
||||
areas: [
|
||||
],
|
||||
rooms: [
|
||||
]
|
||||
},
|
||||
bedSchedule: {
|
||||
dialogVisible: false,
|
||||
selectedBed: null,
|
||||
scheduleHeaders: [
|
||||
{ text: '使用日期', value: 'scheduledate' },
|
||||
{ text: '掛單單號', value: 'guaDanOrderNo' },
|
||||
{ text: '標題', value: 'title' },
|
||||
{ text: '查看掛單', value: 'actions' },
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
methods: {
|
||||
showBedSchedule(bed) {
|
||||
this.bedSchedule.selectedBed = bed;
|
||||
this.bedSchedule.dialogVisible = true;
|
||||
console.log(bed)
|
||||
},
|
||||
closeBedSchedule() {
|
||||
this.bedSchedule.selectedBed = null;
|
||||
this.bedSchedule.dialogVisible = false;
|
||||
},
|
||||
search() {
|
||||
this.GetRegionList();
|
||||
},
|
||||
onStartDateChange() {
|
||||
if (this.filter.endDate && this.filter.endDate < this.filter.startDate) {
|
||||
this.filter.endDate = this.filter.startDate;
|
||||
}
|
||||
if (!this.filter.endDate) {
|
||||
this.filter.endDate = this.filter.startDate;
|
||||
}
|
||||
},
|
||||
resetFilter() {
|
||||
this.filter.startDate = null;
|
||||
this.filter.endDate = null;
|
||||
|
||||
this.filter.selectedArea = null;
|
||||
this.filter.selectedRoom = null;
|
||||
this.filter.occupied = false;
|
||||
this.filter.unoccupied = false;
|
||||
this.filter.Gender = null;
|
||||
},
|
||||
clearFilter() {
|
||||
this.resetFilter();
|
||||
this.GetRegionList();
|
||||
},
|
||||
GetRegionList() {
|
||||
const payload = {
|
||||
startDate: this.filter.startDate,
|
||||
endDate: this.filter.endDate,
|
||||
areaId: this.filter.selectedArea,
|
||||
roomId: this.filter.selectedRoom,
|
||||
occupied: this.filter.occupied,
|
||||
unoccupied: this.filter.unoccupied,
|
||||
gender: this.filter.Gender,
|
||||
};
|
||||
axios.post('/api/region/list', payload)
|
||||
.then((res) => {
|
||||
this.regions = res.data.regions;
|
||||
this.summary = res.data.summary; // 保存後端統計
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('API 錯誤', err);
|
||||
});
|
||||
},
|
||||
toggleOccupied(type) {
|
||||
if (type === "occupied" && this.filter.occupied) {
|
||||
this.filter.unoccupied = false;
|
||||
}
|
||||
if (type === "unoccupied" && this.filter.unoccupied) {
|
||||
this.filter.occupied = false;
|
||||
}
|
||||
},
|
||||
toggleGender(value) {
|
||||
// 如果當前選擇就是自己,則取消選擇
|
||||
this.filter.Gender = this.filter.Gender === value ? null : value;
|
||||
console.log(this.filter.Gender);
|
||||
},
|
||||
getRegionWithRoom() {
|
||||
axios.get('/api/region/regionwithroom')
|
||||
.then((res) => {
|
||||
this.filter.areas = res.data;
|
||||
})
|
||||
},
|
||||
async loadRooms() {
|
||||
try {
|
||||
let url = "/api/room/roomwithbed";
|
||||
const params = {};
|
||||
|
||||
if (this.filter.selectedArea) {
|
||||
params.RegionUuid = this.filter.selectedArea;
|
||||
}
|
||||
|
||||
const res = await axios.get(url, { params });
|
||||
|
||||
this.filter.rooms = res.data; // axios 會自動解析 json
|
||||
this.filter.selectedRoom = null; // 切換區域時,重設房間選擇
|
||||
} catch (error) {
|
||||
console.error("載入房間失敗:", error);
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'filter.selectedArea'() {
|
||||
this.loadRooms();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.GetRegionList();
|
||||
this.getRegionWithRoom();
|
||||
this.loadRooms(); // 預設載入所有房間
|
||||
},
|
||||
computed: {
|
||||
filteredStats() {
|
||||
if (!this.regions || this.regions.length === 0) {
|
||||
return { maleBeds: 0, femaleBeds: 0 };
|
||||
}
|
||||
|
||||
let maleBeds = 0;
|
||||
let femaleBeds = 0;
|
||||
|
||||
this.regions.forEach(region => {
|
||||
(region.room || []).forEach(room => {
|
||||
(room.beds || []).forEach(bed => {
|
||||
if (bed.canuse) {
|
||||
if (bed.gender) maleBeds++;
|
||||
else femaleBeds++;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return { maleBeds, femaleBeds };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
</asp:Content>
|
||||
|
||||
|
||||
14
web/admin/region/bed/index.aspx.cs
Normal file
14
web/admin/region/bed/index.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_region_bed_index : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
956
web/admin/region/index.aspx
Normal file
956
web/admin/region/index.aspx
Normal file
@@ -0,0 +1,956 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="admin_region_index" %>
|
||||
<%@ Register Src="~/admin/_uc/alert.ascx" TagPrefix="uc1" TagName="alert" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server"></asp:Content>
|
||||
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<nav class="mb-2 ps-3">
|
||||
<button class="btn btn-primary me-2" @click="newRegion" type="button">
|
||||
<i class="mdi mdi-plus"></i> 新增主區域
|
||||
</button>
|
||||
<button class="btn btn-secondary me-2" @click="expandAll" type="button">
|
||||
<i class="mdi mdi-arrow-expand-all"></i> 全部展開
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click="collapseAll" type="button">
|
||||
<i class="mdi mdi-arrow-collapse-all"></i> 全部收起
|
||||
</button>
|
||||
</nav>
|
||||
<nav v-if="form && selectedType==null">
|
||||
<button class="btn btn-primary me-2" @click="saveRegion" type="button">
|
||||
<i class="bi bi-save me-1"></i> 儲存區域資料
|
||||
</button>
|
||||
</nav>
|
||||
<nav class="btn-group mb-2 ps-3 pe-3" role="group" v-if="form && selectedType=='region'">
|
||||
|
||||
<button class="btn btn-primary me-2" @click="saveRegion" type="button">
|
||||
<i class="bi bi-save me-1"></i> 儲存區域資料
|
||||
</button>
|
||||
<div v-if="selectedRegionId">
|
||||
<button class="btn btn-success me-2" @click="createSubRegion" type="button" >
|
||||
<i class="mdi mdi-arrow-down-right"></i> 新增下層區域
|
||||
</button>
|
||||
<button class="btn btn-success me-2" type="button" @click="room.showCreateRoomDialog=true" >
|
||||
<i class="mdi mdi-arrow-down-right"></i> 新建客房
|
||||
</button>
|
||||
<button class="btn btn-outline-danger" @click="deleteRegion" type="button">
|
||||
<i class="mdi mdi-delete"></i> 刪除
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="btn-group mb-2 ps-3 pe-3" role="group" v-if="currentSelectRoom && selectedType=='room'">
|
||||
|
||||
<button class="btn btn-primary me-2" type="button" @click="roomUpdate">
|
||||
<i class="bi bi-save me-1"></i> 儲存客房資料
|
||||
</button>
|
||||
<button class="btn btn-outline-danger" @click="confirmRoomDelete" type="button" v-if="currentSelectRoom">
|
||||
<i class="mdi mdi-delete"></i> 刪除客房
|
||||
</button>
|
||||
</nav>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
<uc1:alert runat="server" ID="L_msg" Text="" />
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 col-lg-3">
|
||||
<div class="card shadow-sm my-2">
|
||||
<div class="card-header">區域列表</div>
|
||||
<div class="card-body">
|
||||
<ul class="tree">
|
||||
<li v-for="region in regions" :key="region.uuid">
|
||||
<region-item
|
||||
:item="region"
|
||||
:selected-id="selectedId"
|
||||
:selected-type="selectedType"
|
||||
@select-region="selectRegion"
|
||||
@select-room="selectRoom"
|
||||
:expand-all="expandAllFlag"
|
||||
:collapse-all="collapseAllFlag"
|
||||
@clear-expand-all="expandAllFlag = false"
|
||||
@clear-collapse-all="collapseAllFlag = false"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4 col-lg-6" v-if="!currentSelectRoom">
|
||||
<div class="card shadow-sm my-2"style="position: sticky; top: 20px;">
|
||||
<div class="card-header">
|
||||
<span>区域</span>
|
||||
<span class="fw-bold" v-if="currentSelectRegion">
|
||||
{{ ' ' + currentSelectRegion.name + ' ' }}
|
||||
</span>
|
||||
<span>
|
||||
资料
|
||||
</span>
|
||||
以下 * 欄位為必填欄位
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mt-5">
|
||||
<div class="form-group col-5">
|
||||
<label>區域名稱*</label>
|
||||
<input type="text" class="form-control" v-model="form.name" />
|
||||
</div>
|
||||
<div class="form-group col-5">
|
||||
<label>上層區域</label>
|
||||
<select class="form-control" v-model="form.parentUuid">
|
||||
<option :value="null">無</option>
|
||||
<option v-for="r in flatRegions"
|
||||
:value="r.uuid"
|
||||
:disabled="disabledParentOptions.includes(r.uuid)">{{ r.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-5">
|
||||
<div class="form-group col-5">
|
||||
<label>排序</label>
|
||||
<input type="number" class="form-control" v-model="form.sortOrder" />
|
||||
</div>
|
||||
|
||||
<div class="form-group col-5">
|
||||
<label>區域類型</label>
|
||||
<select class="form-control" v-model="form.regionTypeUuid">
|
||||
<option :value="null">無</option>
|
||||
<option v-for="type in regionTypes" :value="type.uuid">
|
||||
{{ type.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="form-group col-5">
|
||||
<label>客房數量</label>
|
||||
<input type="number" class="form-control" v-model="form.roomCount" />
|
||||
</div>
|
||||
<div class="form-group col-5">
|
||||
<label>描述</label>
|
||||
<textarea class="form-control" v-model="form.description"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group form-check form-switch mb-3 mt-3 col-5">
|
||||
<input type="checkbox" class="form-check-input" id="isActiveCheck" v-model="form.isActive" />
|
||||
<label class="form-check-label" for="isActiveCheck">是否啟用</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- 客房編輯區 -->
|
||||
<div class="col-sm-4 col-lg-6" v-else>
|
||||
<div style="position: sticky; top: 20px;" >
|
||||
<div class="card shadow-sm my-2">
|
||||
<div class="card-header">
|
||||
<span>客房</span>
|
||||
<span class="fw-bold" v-if="currentSelectRoom">
|
||||
{{ ' ' + currentSelectRoom.name + ' ' }}
|
||||
</span>
|
||||
<span>资料</span>
|
||||
以下 * 欄位為必填欄位
|
||||
</div>
|
||||
<div class="card-body shadow">
|
||||
<div class="row mt-5">
|
||||
<div class="form-group col-5">
|
||||
<label>客房名稱*</label>
|
||||
<input v-model="room.room_form.name" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group col-5">
|
||||
<label>床位數量</label>
|
||||
<input v-model="room.room_form.bedCount" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-5">
|
||||
<div class="form-group col-5">
|
||||
<label>客房所属区域</label>
|
||||
<select class="form-control" v-model="room.room_form.regionUuid">
|
||||
<option v-for="r in flatRegions"
|
||||
:value="r.uuid"
|
||||
>{{ r.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group form-check form-switch mb-3 mt-3 col-5 ms-5">
|
||||
<input type="checkbox" class="form-check-input" id="roomIsActive" v-model="room.room_form.isActive" />
|
||||
<label class="form-check-label" for="roomIsActive">是否啟用</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="gender"
|
||||
id="roomMale"
|
||||
:value="true"
|
||||
v-model="room.room_form.gender"
|
||||
required>
|
||||
<label class="form-check-label" for="roomMale">男眾客房</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="gender"
|
||||
id="roomFemale"
|
||||
:value="false"
|
||||
v-model="room.room_form.gender"
|
||||
required>
|
||||
<label class="form-check-label" for="roomFemale">女眾客房</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card shadow-sm my-2" v-if="currentSelectRoom">
|
||||
<div>如果有修改編輯區域的資料,請先儲存後再進行床位操作</div>
|
||||
<div class="card-header d-flex align-items-center justify-content-between">
|
||||
<span class="fw-bold">床位列表</span>
|
||||
<button type="button" class="btn btn-primary" @click="newBed">新增床位</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<v-data-table
|
||||
:headers="room_bed.bed_headers"
|
||||
:items ="room_bed.bed_items"
|
||||
>
|
||||
<template #item.isactive="{item}">
|
||||
{{item.isactive ? '啟用' : '停用'}}
|
||||
</template>
|
||||
<template #item.statusuuid="{item}">
|
||||
{{getBedStatusNameById(item.statusuuid)}}
|
||||
</template>
|
||||
<template #item.action ="{item}">
|
||||
<button type="button" class="btn btn-primary" @click="editBed(item)">
|
||||
編輯
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" @click="deleteBed(item)">
|
||||
刪除
|
||||
</button>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 床位編輯 Modal -->
|
||||
<v-dialog v-model="room_bed.showBedModal" max-width="500px" transition="none">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="headline">{{room_bed.mode == 'create' ? '新增床位' : '編輯床位'}}</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">床位名稱<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" v-model="room_bed.newBedForm.Name" required />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">狀態</label>
|
||||
<select class="form-control" v-model="room_bed.newBedForm.statusuuid">
|
||||
<option v-for="status in room_bed.bed_status" :value="status.uuid">
|
||||
{{status.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input type="checkbox" class="form-check-input" id="isActiveCheck" v-model="room_bed.newBedForm.IsActive" />
|
||||
<label class="form-check-label" for="isActiveCheck">是否啟用</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label d-block">性別 <span class="text-danger">*</span></label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="bedGender"
|
||||
id="bedMale"
|
||||
:value="true"
|
||||
v-model="room_bed.newBedForm.Gender"
|
||||
required>
|
||||
<label class="form-check-label" for="bedMale">男眾床位</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="bedGender"
|
||||
id="bedFemale"
|
||||
:value="false"
|
||||
v-model="room_bed.newBedForm.Gender"
|
||||
required>
|
||||
<label class="form-check-label" for="bedFemale">女眾床位</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="room_bed.showBedModal = false">取消</v-btn>
|
||||
<v-btn color="primary" @click="room_bed.mode === 'create' ? saveBed() : saveEditBed()">儲存</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!--
|
||||
新建客房dialog
|
||||
-->
|
||||
<v-dialog width="50%" max-width="80%" transition="none" v-model="room.showCreateRoomDialog">
|
||||
<v-card style="min-height: 500px; max-height: 80vh; overflow-y: auto;font-size: 30px;">
|
||||
<v-card-title>
|
||||
<span style=" font-size:30px;">
|
||||
新建客房
|
||||
</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="mb-3" style=" font-size:30px;">
|
||||
<label class="form-label">客房名稱<span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" v-model="room.room_form.name" required />
|
||||
</div>
|
||||
<div class="mb-3" style=" font-size:25px;">
|
||||
<label>床位数量</label>
|
||||
<input type="number" class="form-control" v-model="room.room_form.bedCount" />
|
||||
</div>
|
||||
<div class="form-check form-switch mb-3" style=" font-size:25px;">
|
||||
<input type="checkbox" class="form-check-input" v-model="room.room_form.isActive" />
|
||||
<label class="form-check-label">是否啟用</label>
|
||||
</div>
|
||||
<div class="mb-3" style=" font-size:25px;">
|
||||
<label class="form-label d-block">性別 <span class="text-danger">*</span></label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="creatRoomGender"
|
||||
v-model="room.room_form.gender"
|
||||
:value="1"
|
||||
required>
|
||||
<label class="form-check-label" style=" font-size:25px;">男眾客房</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline" style=" font-size:25px;">
|
||||
<input class="form-check-input"
|
||||
type="radio"
|
||||
name="creatRoomGender"
|
||||
v-model="room.room_form.gender"
|
||||
:value="0"
|
||||
required>
|
||||
<label class="form-check-label" style=" font-size:25px;">女眾客房</label>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="room.showCreateRoomDialog = false" style=" font-size:25px;">取消</v-btn>
|
||||
<v-btn color="primary" @click="roomCreate" style=" font-size:25px;">儲存</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<!-- 更新修改確認彈出視窗 -->
|
||||
<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>
|
||||
.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 in item.children" :key="child.uuid">
|
||||
<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 +'床)'}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
vuetify: new Vuetify(vuetify_options),
|
||||
data() {
|
||||
return {
|
||||
selectedId: null, // 被選中項目ID
|
||||
selectedType: null, // 'region' 或 'room'
|
||||
expandAllFlag: false, // 控制全部展開
|
||||
collapseAllFlag: false, // 控制全部收起
|
||||
showModal: false,
|
||||
showDeleteModal: false,
|
||||
modalMessage: '',
|
||||
deleteModalMessage: '',
|
||||
selectedRegionId: null,
|
||||
regions: [],
|
||||
flatRegions: [],
|
||||
disabledParentOptions: [],
|
||||
regionTypes: [],
|
||||
currentSelectRegion: null,
|
||||
currentSelectRoom: null,
|
||||
form: {
|
||||
uuid: null,
|
||||
name: '',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
parentUuid: null,
|
||||
regionTypeUuid: null,
|
||||
isActive: true,
|
||||
roomCount: null,
|
||||
},
|
||||
room: {
|
||||
//room相關的變數放在這裡
|
||||
room_form: {
|
||||
uuid: null,
|
||||
name: '',
|
||||
gender: null,
|
||||
isActive: true,
|
||||
regionUuid: null,
|
||||
bedCount: null,
|
||||
regionUuid: null,
|
||||
},
|
||||
showCreateRoomDialog: false, //显示新建客房对话框
|
||||
},
|
||||
room_bed: {
|
||||
mode: 'create', // 'create' or 'edit'
|
||||
bed_items: [],
|
||||
bed_headers: [
|
||||
{ text: '床位編號', value: 'uuid' },
|
||||
{ text: '床位名稱', value: 'name' },
|
||||
{ text: '床位狀態', value: 'statusuuid' },
|
||||
{ text: '是否啟用', value: 'isactive' },
|
||||
{ text: '', value: 'action' },
|
||||
],
|
||||
newBedForm: {
|
||||
uuid: null,
|
||||
RegionUuid: null,
|
||||
Name: '',
|
||||
statusuuid: null,
|
||||
IsActive: true,
|
||||
Gender: null,
|
||||
},
|
||||
showBedModal: false,
|
||||
bed_status: [], //床位狀態列表
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
expandAll() {
|
||||
this.expandAllFlag = true;
|
||||
this.collapseAllFlag = false;
|
||||
},
|
||||
collapseAll() {
|
||||
this.collapseAllFlag = true;
|
||||
this.expandAllFlag = false;
|
||||
},
|
||||
async loadRegions() {
|
||||
const res = await axios.post('/api/region/getRegionList');
|
||||
this.regions = res.data;
|
||||
this.flatRegions = this.flatten(res.data);
|
||||
if (this.currentSelectRoom) {
|
||||
|
||||
}
|
||||
},
|
||||
loadRegionType() {
|
||||
axios.post('/api/region/getRegionType')
|
||||
.then(res => {
|
||||
this.regionTypes = res.data
|
||||
});
|
||||
},
|
||||
flatten(data, list = []) {
|
||||
data.forEach(item => {
|
||||
list.push({ uuid: item.uuid, name: item.name });
|
||||
if (item.children && item.children.length) {
|
||||
this.flatten(item.children, list);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
},
|
||||
selectRegion(region) {
|
||||
this.selectedId = region.uuid;
|
||||
this.selectedType = 'region';
|
||||
this.selectedRegionId = region.uuid;
|
||||
this.currentSelectRegion = region;
|
||||
this.currentSelectRoom = null;
|
||||
this.resetRoomForm();
|
||||
this.form = {
|
||||
uuid: region.uuid,
|
||||
name: region.name,
|
||||
description: region.description,
|
||||
sortOrder: region.sortOrder,
|
||||
parentUuid: region.parentUuid,
|
||||
regionTypeUuid: region.regionTypeUuid,
|
||||
isActive: region.isActive !== false, // 若為 null 也視為啟用
|
||||
roomCount: region.roomCount,
|
||||
};
|
||||
// 禁止選擇自己與所有子孫作為上層
|
||||
const node = this.findRegionById(this.regions, region.uuid);
|
||||
this.disabledParentOptions = this.getAllDescendants(node);
|
||||
},
|
||||
|
||||
newRegion() {
|
||||
this.selectedRegionId = null;
|
||||
this.form = {
|
||||
uuid: null,
|
||||
name: '',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
parentUuid: null,
|
||||
isActive: true
|
||||
};
|
||||
this.disabledParentOptions = [];
|
||||
this.currentSelectRegion = null;
|
||||
this.currentSelectRoom = null;
|
||||
},
|
||||
createSubRegion() {
|
||||
if (!this.selectedRegionId) return;
|
||||
this.form = {
|
||||
uuid: null,
|
||||
name: '',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
parentUuid: this.selectedRegionId,
|
||||
isActive: true
|
||||
|
||||
};
|
||||
},
|
||||
saveRegion() {
|
||||
if (!this.form.name || this.form.name.trim() === '') {
|
||||
this.$refs.messageModal.open({
|
||||
title: "錯誤",
|
||||
message: "請輸入區域名稱",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const url = this.form.uuid ? '/api/region/update' : '/api/region/create';
|
||||
axios.post(url, this.form)
|
||||
.then((res) => {
|
||||
//alert('儲存成功');
|
||||
this.loadRegions();
|
||||
//this.newRegion();
|
||||
this.form.uuid = res.data.uuid;
|
||||
this.selectedRegionId = res.data.uuid;
|
||||
this.currentSelectRegion = JSON.parse(JSON.stringify(this.form));
|
||||
this.$refs.messageModal.open({
|
||||
title: "更新",
|
||||
message: "更新成功",
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$refs.messageModal.open({
|
||||
title: '更新提示',
|
||||
message: error.response?.data?.message || "儲存失敗,請稍後再試。",
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteRegion() {
|
||||
this.$refs.confirmModal.open({
|
||||
titil: '刪除提示',
|
||||
message: '確定要刪除該區域嗎?刪除區域會一併刪除該區域下的所有區域和床位',
|
||||
onConfirm: () => {
|
||||
this.confirmDeleteRegion();
|
||||
}
|
||||
});
|
||||
},
|
||||
confirmDeleteRegion() {
|
||||
axios.post('/api/region/delete', { uuid: this.form.uuid })
|
||||
.then(() => {
|
||||
this.showDeleteModal = false;
|
||||
this.$refs.messageModal.open({
|
||||
title: "刪除",
|
||||
message: "刪除成功",
|
||||
});
|
||||
this.room_bed.bed_items = [];
|
||||
this.currentSelectRegion = null;
|
||||
this.currentSelectRoom = null;
|
||||
this.loadRegions();
|
||||
this.newRegion();
|
||||
});
|
||||
},
|
||||
|
||||
getAllDescendants(node) {
|
||||
//尋找某個區域的所有子區域
|
||||
const ids = [];
|
||||
const dfs = (n) => {
|
||||
ids.push(n.uuid);
|
||||
if (n.children) {
|
||||
n.children.forEach(child => dfs(child));
|
||||
}
|
||||
};
|
||||
dfs(node);
|
||||
return ids;
|
||||
},
|
||||
findRegionById(list, uuid) {
|
||||
for (const item of list) {
|
||||
if (item.uuid === uuid) return item;
|
||||
if (item.children) {
|
||||
const found = this.findRegionById(item.children, uuid);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
//下面是床位相關的操作函數
|
||||
newBed() {
|
||||
//添加床位
|
||||
if (!this.currentSelectRoom) {
|
||||
this.$refs.messageModal.open({
|
||||
title: '提示',
|
||||
message: '請先選擇一個客房'
|
||||
})
|
||||
return;
|
||||
}
|
||||
this.room_bed.mode = 'create';
|
||||
this.room_bed.newBedForm = {
|
||||
uuid: null,
|
||||
RoomUuid: this.currentSelectRoom.uuid,
|
||||
Name: '',
|
||||
statusuuid: null,
|
||||
IsActive: true,
|
||||
Gender: this.currentSelectRoom.gender, // 不設預設值,強制選擇
|
||||
};
|
||||
this.room_bed.showBedModal = true;
|
||||
},
|
||||
async saveBed() {
|
||||
if (!this.room_bed.newBedForm.Name || this.room_bed.newBedForm.Name.trim() === '') {
|
||||
this.$refs.messageModal.open({
|
||||
title: '錯誤',
|
||||
message: '請輸入床位名稱'
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var res = await axios.post('/api/region/bed/create', this.room_bed.newBedForm);
|
||||
this.room_bed.showBedModal = false;
|
||||
this.$refs.messageModal.open({
|
||||
title: '成功',
|
||||
message: '新增床位成功'
|
||||
});
|
||||
// 重新載入區域資料
|
||||
await this.loadRegions();
|
||||
this.room_bed.bed_items.push({
|
||||
...res.data
|
||||
})
|
||||
} catch (err) {
|
||||
this.$refs.messageModal.open({
|
||||
title: '錯誤',
|
||||
message: err.response?.data?.message || '新增失敗'
|
||||
});
|
||||
}
|
||||
},
|
||||
deleteBed(bed) {
|
||||
this.$refs.confirmModal.open({
|
||||
titil: '刪除提示',
|
||||
message: '確定要刪除該床位嗎',
|
||||
onConfirm: () => {
|
||||
this.confirmDeleteBed(bed);
|
||||
}
|
||||
});
|
||||
},
|
||||
confirmDeleteBed(bed) {
|
||||
axios.post('/api/region/bed/delete', null, {
|
||||
params: { uuid: bed.uuid }
|
||||
}) // 假設後端吃的是 id
|
||||
.then(() => {
|
||||
// 成功後從本地列表移除
|
||||
this.room_bed.bed_items = this.room_bed.bed_items.filter(item => item.uuid !== bed.uuid);
|
||||
this.$refs.messageModal.open({
|
||||
title: '刪除成功',
|
||||
message: `已刪除床位「${bed.name}」`
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
this.$refs.messageModal.open({
|
||||
title: '錯誤',
|
||||
message: err.response?.data?.message || '刪除失敗'
|
||||
});
|
||||
});
|
||||
},
|
||||
editBed(bed) {
|
||||
if (!this.currentSelectRoom) {
|
||||
this.$refs.messageModal.open({
|
||||
title: '提示',
|
||||
message: '請先選擇一個客房'
|
||||
})
|
||||
return;
|
||||
}
|
||||
this.room_bed.mode = 'edit';
|
||||
this.room_bed.newBedForm = {
|
||||
uuid: bed.uuid,
|
||||
RegionUuid: bed.regionUuid,
|
||||
RoomUuid: bed.roomUuid,
|
||||
Name: bed.name,
|
||||
statusuuid: bed.statusuuid,
|
||||
IsActive: bed.isactive,
|
||||
Gender: bed.gender,
|
||||
};
|
||||
this.room_bed.showBedModal = true;
|
||||
},
|
||||
async saveEditBed() {
|
||||
|
||||
try {
|
||||
await axios.post('/api/region/bed/update', this.room_bed.newBedForm);
|
||||
this.room_bed.showBedModal = false;
|
||||
|
||||
const updated = this.room_bed.newBedForm;
|
||||
const index = this.room_bed.bed_items.findIndex(b => b.uuid === updated.uuid);
|
||||
|
||||
if (index !== -1) {
|
||||
this.$set(this.room_bed.bed_items, index, {
|
||||
...this.room_bed.bed_items[index], // 保留原本未更新欄位
|
||||
roomUuid: updated.RoomUuid,
|
||||
name: updated.Name,
|
||||
statusuuid: updated.statusuuid,
|
||||
isactive: updated.IsActive
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.$refs.messageModal.open({
|
||||
title: '成功',
|
||||
message: '更新床位成功'
|
||||
});
|
||||
await this.loadRegions();
|
||||
this.room_bed.bed_items = this.currentSelectRoom.beds;
|
||||
//this.selectRegion(this.findRegionById(this.regions, this.form.id));
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
this.$refs.messageModal.open({
|
||||
title: '錯誤',
|
||||
message: err.response?.data?.message || '更新失敗'
|
||||
});
|
||||
}
|
||||
},
|
||||
getBedStatus() {
|
||||
//獲取床位狀態
|
||||
axios.get('/api/region/bed/status/list')
|
||||
.then((res) => {
|
||||
this.room_bed.bed_status = res.data;
|
||||
})
|
||||
},
|
||||
getBedStatusNameById(id) {
|
||||
console.log(id)
|
||||
//傳入一個Id,獲取該Id對應的名稱
|
||||
const status = this.room_bed.bed_status.find(i => i.uuid == id);
|
||||
if (status) {
|
||||
return status.name;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
//------------------------------------------------
|
||||
//room相關的函數
|
||||
roomGet() {
|
||||
|
||||
},
|
||||
roomCreate() {
|
||||
this.room.room_form['regionUuid'] = this.currentSelectRegion.uuid
|
||||
if (this.room.room_form.gender === null) {
|
||||
this.$refs.messageModal.open({
|
||||
message: "請選擇性別"
|
||||
});
|
||||
return;
|
||||
}
|
||||
axios.post('/api/region/room/create', this.room.room_form)
|
||||
.then((res) => {
|
||||
this.room.showCreateRoomDialog = false;
|
||||
this.currentSelectRegion.rooms.push(res.data);
|
||||
this.$refs.messageModal.open({
|
||||
title: '提示',
|
||||
message: '客房新建成功',
|
||||
});
|
||||
this.resetRoomForm();
|
||||
}).catch((error) => {
|
||||
this.$refs.messageModal.open({
|
||||
message: (error.response?.data?.message || error.message)
|
||||
});
|
||||
})
|
||||
},
|
||||
async roomUpdate() {
|
||||
try {
|
||||
const res = await axios.post('/api/region/room/update', this.room.room_form);
|
||||
this.$refs.messageModal.open({
|
||||
message: '客房資料更新成功'
|
||||
});
|
||||
this.loadRegions();
|
||||
this.currentSelectRoom = {
|
||||
...this.room.room_form
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('更新失敗', error);
|
||||
this.$refs.messageModal.open({
|
||||
message: (error.response?.data?.message || error.message)
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
roomDelete() {
|
||||
axios.post('/api/region/room/delete', { uuid: this.currentSelectRoom.uuid })
|
||||
.then((res) => {
|
||||
const region = this.findRegionById(this.regions, this.currentSelectRoom.regionUuid)//當前room所在的region
|
||||
if (region) {
|
||||
const index = region.rooms.findIndex(item => item.uuid === this.currentSelectRoom.uuid)
|
||||
if (index !== -1) {
|
||||
|
||||
region.rooms.splice(index, 1); // 直接修改原數組
|
||||
}
|
||||
}
|
||||
this.currentSelectRoom = null;
|
||||
this.room_bed.bed_items = [];
|
||||
//清空 beds
|
||||
});
|
||||
},
|
||||
confirmRoomDelete() {
|
||||
this.$refs.confirmModal.open({
|
||||
title: '確認刪除',
|
||||
message: '確認刪除該房間嗎?刪除房間會一併刪除房間床位,刪除後不可恢復',
|
||||
onConfirm: () => {
|
||||
this.roomDelete();
|
||||
}
|
||||
})
|
||||
},
|
||||
selectRoom(room) {
|
||||
this.selectedId = room.uuid;
|
||||
this.selectedType = 'room';
|
||||
this.currentSelectRoom = room;
|
||||
this.currentSelectRegion = null;
|
||||
this.room.room_form = {
|
||||
...this.currentSelectRoom
|
||||
}
|
||||
this.room_bed.bed_items = this.currentSelectRoom.beds;
|
||||
},
|
||||
resetRoomForm() {
|
||||
this.room.room_form = {
|
||||
uuid: null,
|
||||
name: '',
|
||||
gender: null,
|
||||
isActive: true,
|
||||
regionUuid: null,
|
||||
bedCount: null,
|
||||
}
|
||||
},
|
||||
//結束room相關的函數
|
||||
//------------------------------------------------
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.loadRegions();
|
||||
this.loadRegionType();
|
||||
this.getBedStatus();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</asp:Content>
|
||||
19
web/admin/region/index.aspx.cs
Normal file
19
web/admin/region/index.aspx.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_region_index : MyWeb.config
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
95
web/admin/regiontype/index.aspx
Normal file
95
web/admin/regiontype/index.aspx
Normal file
@@ -0,0 +1,95 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="index.aspx.cs" Inherits="admin_regiontype_index" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<a href="reg.aspx" class="btn btn-primary">
|
||||
<i class="mdi mdi-plus"></i>新增區域類型
|
||||
</a>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
<div class="container">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">區域類型管理</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="items"
|
||||
>
|
||||
<template #item.isactive =" {item} ">
|
||||
<v-icon small :color="item.isactive ? 'success' : 'grey'">
|
||||
{{ item.isactive ? 'mdi-check-circle' : 'mdi-close-circle' }}
|
||||
</v-icon>
|
||||
{{ item.isactive ? '已啟用' : '已停用' }}
|
||||
</template>
|
||||
<template #item.actions="{item}">
|
||||
<a :href="'reg.aspx?regiontypeid='+item.uuid" class="btn btn-primary"><i class="mdi mdi-pencil"></i>修改</a>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-danger"
|
||||
@click="regiontypeDelete(item)"
|
||||
>
|
||||
<i class="mdi mdi-delete"></i> 刪除
|
||||
</button>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<message-modal ref="messageModal"></message-modal>
|
||||
<confirm-modal ref="confirmModal"></confirm-modal>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
vuetify: new Vuetify(vuetify_options),
|
||||
data() {
|
||||
return {
|
||||
headers: [
|
||||
{ text: '區域類型名稱', value: 'name' },
|
||||
{ text: '區域代碼', value: 'code' },
|
||||
{ text: '啟用', value: 'isactive' },
|
||||
{ text: '操作', value: 'actions', sortable: false }
|
||||
],
|
||||
items: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getRegionTypeList() {
|
||||
axios.post('/api/regiontype/getreiontypelist')
|
||||
.then((res) => {
|
||||
this.items = res.data;
|
||||
})
|
||||
},
|
||||
regiontypeDelete(item) {
|
||||
this.$refs.confirmModal.open({
|
||||
'title': '刪除提示',
|
||||
'message': `確定要刪除 ${item.name} ?`,
|
||||
onConfirm: () => {
|
||||
axios.post('/api/regiontype/delete',null, {
|
||||
params: { uuid: item.uuid }
|
||||
})
|
||||
.then(() => {
|
||||
this.items = this.items.filter(i => i.uuid != item.uuid);
|
||||
this.$refs.messageModal.open({
|
||||
title: '操作成功',
|
||||
message: '刪除成功!',
|
||||
status: 'success'
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getRegionTypeList();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</asp:Content>
|
||||
|
||||
14
web/admin/regiontype/index.aspx.cs
Normal file
14
web/admin/regiontype/index.aspx.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_regiontype_index : MyWeb.config
|
||||
{
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
46
web/admin/regiontype/reg.aspx
Normal file
46
web/admin/regiontype/reg.aspx
Normal file
@@ -0,0 +1,46 @@
|
||||
<%@ Page Title="" Language="C#" MasterPageFile="~/admin/Templates/TBS5ADM001/MasterPage.master" AutoEventWireup="true" CodeFile="reg.aspx.cs" Inherits="admin_regiontype_reg" %>
|
||||
|
||||
<asp:Content ID="Content1" ContentPlaceHolderID="page_header" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content2" ContentPlaceHolderID="page_nav" Runat="Server">
|
||||
<div></div>
|
||||
<div>
|
||||
<a href="index.aspx" class="btn btn-primary">回列表</a>
|
||||
</div>
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
|
||||
<div class="container">
|
||||
<div class="card shadow-sm my-3">
|
||||
<div class="card-header">
|
||||
<asp:Literal ID="L_title" runat="server" Text="新增區域類型" />
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<asp:Literal ID="L_msg" runat="server" />
|
||||
<asp:HiddenField ID="HF_Id" runat="server" />
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="TB_Name">名稱</label>
|
||||
<asp:TextBox ID="TB_Name" runat="server" CssClass="form-control" />
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<label for="TB_Name">代碼</label>
|
||||
<asp:TextBox ID="TB_Code" runat="server" CssClass="form-control" />
|
||||
</div>
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input type="checkbox" class="form-check-input" id="CB_IsActive" name="CB_IsActive" checked runat="server"/>
|
||||
<label class="form-check-label" for="CB_IsActive">是否啟用</label>
|
||||
</div>
|
||||
|
||||
|
||||
<asp:Button ID="BTN_Save" runat="server" Text="儲存" CssClass="btn btn-primary" OnClick="BTN_Save_Click" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ID="Content4" ContentPlaceHolderID="offCanvasRight" Runat="Server">
|
||||
</asp:Content>
|
||||
<asp:Content ID="Content5" ContentPlaceHolderID="footer_script" Runat="Server">
|
||||
</asp:Content>
|
||||
|
||||
87
web/admin/regiontype/reg.aspx.cs
Normal file
87
web/admin/regiontype/reg.aspx.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
public partial class admin_regiontype_reg : MyWeb.config
|
||||
{
|
||||
private Model.ezEntities _db = new Model.ezEntities();
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
if (Guid.TryParse(Request.QueryString["regiontypeid"], out Guid id))
|
||||
{
|
||||
LoadData(id);
|
||||
L_title.Text = "編輯區域類型";
|
||||
}
|
||||
}
|
||||
}
|
||||
private void LoadData(Guid id)
|
||||
{
|
||||
var rt = _db.RegionType.FirstOrDefault(r => r.Uuid == id);
|
||||
if (rt == null)
|
||||
{
|
||||
L_msg.Text = "<div class='alert alert-danger'>找不到資料</div>";
|
||||
BTN_Save.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
HF_Id.Value = rt.Uuid.ToString();
|
||||
TB_Name.Text = rt.Name;
|
||||
TB_Code.Text = rt.Code;
|
||||
CB_IsActive.Checked = rt.IsActive;
|
||||
}
|
||||
|
||||
protected void BTN_Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
RegionType rt;
|
||||
if (Guid.TryParse(HF_Id.Value, out Guid id))
|
||||
{
|
||||
// 更新
|
||||
rt = _db.RegionType.FirstOrDefault(r => r.Uuid == id);
|
||||
if (rt == null)
|
||||
{
|
||||
L_msg.Text = "<div class='alert alert-danger'>資料不存在</div>";
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 新增
|
||||
rt = new RegionType();
|
||||
rt.Uuid = Guid.NewGuid();
|
||||
_db.RegionType.Add(rt);
|
||||
}
|
||||
|
||||
rt.Name = TB_Name.Text.Trim();
|
||||
if(rt.Name.Length == 0)
|
||||
{
|
||||
L_msg.Text = "<div class='alert alert-danger'>名稱不能为空</div>";
|
||||
return;
|
||||
}
|
||||
rt.Code = TB_Code.Text.Trim();
|
||||
rt.IsActive = CB_IsActive.Checked;
|
||||
|
||||
_db.SaveChanges();
|
||||
|
||||
L_msg.Text = "<div class='alert alert-success'>儲存成功</div>";
|
||||
|
||||
// 如果是新增,更新隱藏欄位並切換標題為編輯
|
||||
if (HF_Id.Value == "")
|
||||
{
|
||||
HF_Id.Value = rt.Uuid.ToString();
|
||||
L_title.Text = "編輯區域類型";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
L_msg.Text = $"<div class='alert alert-danger'>錯誤:{ex.Message}</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user