This commit is contained in:
zpc 2025-12-07 15:15:37 +08:00
parent 7468f0df26
commit 2fe3facdce
8 changed files with 1886 additions and 552 deletions

View File

@ -2796,5 +2796,33 @@ namespace CoreCms.Net.Configuration
}
#endregion
#region ===============================================================
/// <summary>
/// 房间不可用类型
/// </summary>
public enum RoomUnavailableType
{
/// <summary>
/// 禁用/维护
/// </summary>
[Description("禁用/维护")]
Disabled = 1,
/// <summary>
/// 后台预约
/// </summary>
[Description("后台预约")]
BackendReservation = 2,
/// <summary>
/// 线下预约
/// </summary>
[Description("线下预约")]
OfflineReservation = 3
}
#endregion
}
}

View File

@ -93,6 +93,12 @@ namespace CoreCms.Net.Model.Entities
[Display(Name = "时段类型")]
public System.Int32? time_slot_type { get; set; }
/// <summary>
/// 不可用类型1-禁用/维护, 2-后台预约, 3-线下预约
/// </summary>
[Display(Name = "不可用类型")]
public System.Int32 type { get; set; } = 1;
/// <summary>
/// 创建人ID管理员
/// </summary>

View File

@ -380,6 +380,11 @@ namespace CoreCms.Net.Model.ViewModels.SQ
/// </summary>
public string price_desc_member { get; set; }
/// <summary>
/// 不可用类型当status为unavailable时有效1=禁用/维护2=后台预约3=线下预约
/// </summary>
public int? unavailable_type { get; set; }
/// <summary>
/// 该时段内的预约列表(仅详情接口返回)
/// </summary>

View File

@ -243,14 +243,15 @@ namespace CoreCms.Net.Services
}
else
{
// 检查不可用时段
bool isUnavailable = unavailableTimes?.Any(u =>
// 检查不可用时段,并获取禁用类型
var unavailableRecord = unavailableTimes?.FirstOrDefault(u =>
(u.time_slot_type.HasValue && u.time_slot_type.Value == slotType) ||
(u.start_time < slotEnd && u.end_time > slotStart)) ?? false;
(u.start_time < slotEnd && u.end_time > slotStart));
if (isUnavailable)
if (unavailableRecord != null)
{
slotDto.status = GlobalConstVars.RoomStatusUnavailable;
slotDto.unavailable_type = unavailableRecord.type; // 设置禁用类型
}
else
{

View File

@ -57,6 +57,7 @@ namespace CoreCms.Net.Web.Admin.Controllers
private readonly ISQRoomsServices _SQRoomsServices;
private readonly ISQReservationsServices _SQReservationsServices;
private readonly ISQReservationParticipantsServices _SQReservationParticipantsServices;
private readonly ISQRoomUnavailableTimesServices _SQRoomUnavailableTimesServices;
private readonly ICoreCmsUserServices _userServices;
private readonly IMapper _mapper;
@ -68,6 +69,7 @@ namespace CoreCms.Net.Web.Admin.Controllers
, ISQRoomsServices SQRoomsServices
, ISQReservationsServices SQReservationsServices
, ISQReservationParticipantsServices SQReservationParticipantsServices
, ISQRoomUnavailableTimesServices SQRoomUnavailableTimesServices
, ICoreCmsUserServices userServices
, IMapper mapper
)
@ -77,6 +79,7 @@ namespace CoreCms.Net.Web.Admin.Controllers
_SQRoomsServices = SQRoomsServices;
_SQReservationsServices = SQReservationsServices;
_SQReservationParticipantsServices = SQReservationParticipantsServices;
_SQRoomUnavailableTimesServices = SQRoomUnavailableTimesServices;
_userServices = userServices;
_mapper = mapper;
}
@ -1098,6 +1101,545 @@ namespace CoreCms.Net.Web.Admin.Controllers
}
#endregion
#region ============================================================
// POST: Api/SQRoomPricing/SetRoomUnavailable
/// <summary>
/// 设置房间时段不可用
/// </summary>
/// <param name="roomId">房间ID</param>
/// <param name="date">日期</param>
/// <param name="timeSlotType">时段类型0=凌晨1=上午2=下午3=晚上)</param>
/// <param name="type">不可用类型1=禁用/维护2=后台预约3=线下预约)</param>
/// <param name="reason">原因/备注</param>
/// <returns></returns>
[HttpPost]
[Description("设置房间时段不可用")]
public async Task<AdminUiCallBack> SetRoomUnavailable([FromBody] SetRoomUnavailableRequest request)
{
var jm = new AdminUiCallBack();
try
{
// 参数验证
if (request.roomId <= 0)
{
jm.msg = "请选择房间";
return jm;
}
if (string.IsNullOrEmpty(request.date))
{
jm.msg = "请选择日期";
return jm;
}
if (!DateTime.TryParse(request.date, out DateTime dateValue))
{
jm.msg = "日期格式不正确";
return jm;
}
if (request.timeSlotType < 0 || request.timeSlotType > 3)
{
jm.msg = "时段类型不正确";
return jm;
}
if (request.type < 1 || request.type > 3)
{
jm.msg = "不可用类型不正确";
return jm;
}
// 检查房间是否存在
var room = await _SQRoomsServices.QueryByIdAsync(request.roomId);
if (room == null)
{
jm.msg = "房间不存在";
return jm;
}
// 根据时段类型计算开始和结束时间
var (startTime, endTime) = GetTimeSlotRange(dateValue, request.timeSlotType);
// 检查该时段是否已有用户预约
var hasReservation = await _SQReservationsServices.ExistsAsync(
r => r.room_id == request.roomId &&
r.status < 4 && // 排除已取消的预约
r.start_time < endTime &&
r.end_time > startTime
);
if (hasReservation)
{
jm.msg = "该时段已有用户预约,请先处理预约后再设置禁用";
return jm;
}
// 检查是否已存在禁用记录
var existingRecord = await _SQRoomUnavailableTimesServices.QueryByClauseAsync(
u => u.room_id == request.roomId &&
u.time_slot_type == request.timeSlotType &&
u.start_time == startTime &&
u.end_time == endTime,
u => u.id,
OrderByType.Asc
);
if (existingRecord != null)
{
// 更新现有记录
existingRecord.type = request.type;
existingRecord.reason = request.reason;
await _SQRoomUnavailableTimesServices.UpdateAsync(existingRecord);
jm.code = 0;
jm.msg = "更新成功";
}
else
{
// 新增记录
var newRecord = new SQRoomUnavailableTimes
{
room_id = request.roomId,
start_time = startTime,
end_time = endTime,
time_slot_type = request.timeSlotType,
type = request.type,
reason = request.reason,
created_at = DateTime.Now
};
await _SQRoomUnavailableTimesServices.InsertAsync(newRecord);
jm.code = 0;
jm.msg = "设置成功";
}
}
catch (Exception ex)
{
jm.msg = "设置失败:" + ex.Message;
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "设置房间不可用", "SetRoomUnavailable", ex);
}
return jm;
}
/// <summary>
/// 根据时段类型获取时间范围
/// </summary>
private (DateTime startTime, DateTime endTime) GetTimeSlotRange(DateTime date, int timeSlotType)
{
var baseDate = date.Date;
return timeSlotType switch
{
0 => (baseDate, baseDate.AddHours(6)), // 凌晨 00:00-06:00
1 => (baseDate.AddHours(6), baseDate.AddHours(12)), // 上午 06:00-12:00
2 => (baseDate.AddHours(12), baseDate.AddHours(18)), // 下午 12:00-18:00
3 => (baseDate.AddHours(18), baseDate.AddDays(1)), // 晚上 18:00-24:00
_ => (baseDate, baseDate.AddDays(1))
};
}
#endregion
#region ============================================================
// POST: Api/SQRoomPricing/CancelRoomUnavailable
/// <summary>
/// 取消房间时段不可用
/// </summary>
/// <param name="id">不可用记录ID</param>
/// <returns></returns>
[HttpPost]
[Description("取消房间时段不可用")]
public async Task<AdminUiCallBack> CancelRoomUnavailable([FromBody] CancelRoomUnavailableRequest request)
{
var jm = new AdminUiCallBack();
try
{
if (request.ids == null || request.ids.Length == 0)
{
jm.msg = "请选择要取消的记录";
return jm;
}
await _SQRoomUnavailableTimesServices.DeleteByIdsAsync(request.ids);
jm.code = 0;
jm.msg = "取消成功";
}
catch (Exception ex)
{
jm.msg = "取消失败:" + ex.Message;
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "取消房间不可用", "CancelRoomUnavailable", ex);
}
return jm;
}
#endregion
#region ============================================================
// POST: Api/SQRoomPricing/CancelRoomUnavailableAllDay
/// <summary>
/// 取消房间整天不可用(删除该日期的所有禁用记录)
/// </summary>
/// <param name="roomId">房间ID</param>
/// <param name="date">日期</param>
/// <returns></returns>
[HttpPost]
[Description("取消房间整天不可用")]
public async Task<AdminUiCallBack> CancelRoomUnavailableAllDay([FromBody] CancelRoomUnavailableAllDayRequest request)
{
var jm = new AdminUiCallBack();
try
{
// 参数验证
if (request.roomId <= 0)
{
jm.msg = "请选择房间";
return jm;
}
if (string.IsNullOrEmpty(request.date))
{
jm.msg = "请选择日期";
return jm;
}
if (!DateTime.TryParse(request.date, out DateTime dateValue))
{
jm.msg = "日期格式不正确";
return jm;
}
// 检查房间是否存在
var room = await _SQRoomsServices.QueryByIdAsync(request.roomId);
if (room == null)
{
jm.msg = "房间不存在";
return jm;
}
// 获取该日期的开始和结束时间
var startOfDay = dateValue.Date;
var endOfDay = dateValue.Date.AddDays(1);
// 查询该房间该日期的所有禁用记录
var unavailableRecords = await _SQRoomUnavailableTimesServices.QueryListByClauseAsync(
u => u.room_id == request.roomId &&
u.start_time >= startOfDay &&
u.start_time < endOfDay
);
if (unavailableRecords == null || unavailableRecords.Count == 0)
{
jm.msg = "该日期没有禁用记录";
return jm;
}
// 获取所有记录的ID
var ids = unavailableRecords.Select(r => r.id).ToArray();
// 批量删除
await _SQRoomUnavailableTimesServices.DeleteByIdsAsync(ids);
jm.code = 0;
jm.msg = $"取消成功,共删除 {ids.Length} 条禁用记录";
}
catch (Exception ex)
{
jm.msg = "取消失败:" + ex.Message;
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "取消房间整天不可用", "CancelRoomUnavailableAllDay", ex);
}
return jm;
}
#endregion
#region ============================================================
// POST: Api/SQRoomPricing/SetRoomUnavailableAllDay
/// <summary>
/// 设置房间整天不可用(禁用所有时段,类型为维护)
/// </summary>
/// <param name="roomId">房间ID</param>
/// <param name="date">日期</param>
/// <returns></returns>
[HttpPost]
[Description("设置房间整天不可用")]
public async Task<AdminUiCallBack> SetRoomUnavailableAllDay([FromBody] SetRoomUnavailableAllDayRequest request)
{
var jm = new AdminUiCallBack();
try
{
// 参数验证
if (request.roomId <= 0)
{
jm.msg = "请选择房间";
return jm;
}
if (string.IsNullOrEmpty(request.date))
{
jm.msg = "请选择日期";
return jm;
}
if (!DateTime.TryParse(request.date, out DateTime dateValue))
{
jm.msg = "日期格式不正确";
return jm;
}
// 检查房间是否存在
var room = await _SQRoomsServices.QueryByIdAsync(request.roomId);
if (room == null)
{
jm.msg = "房间不存在";
return jm;
}
// 检查该日期是否已有用户预约
var startOfDay = dateValue.Date;
var endOfDay = dateValue.Date.AddDays(1);
var hasReservation = await _SQReservationsServices.ExistsAsync(
r => r.room_id == request.roomId &&
r.status < 4 && // 排除已取消的预约
r.start_time < endOfDay &&
r.end_time > startOfDay
);
if (hasReservation)
{
jm.msg = "该日期已有用户预约,请先处理预约后再设置禁用";
return jm;
}
// 禁用类型为维护type=1原因默认为"整天维护"
var maintenanceType = 1;
var defaultReason = "整天维护";
// 遍历所有4个时段设置禁用
var successCount = 0;
var updateCount = 0;
var insertCount = 0;
for (int timeSlotType = 0; timeSlotType <= 3; timeSlotType++)
{
var (startTime, endTime) = GetTimeSlotRange(dateValue, timeSlotType);
// 检查是否已存在禁用记录
var existingRecord = await _SQRoomUnavailableTimesServices.QueryByClauseAsync(
u => u.room_id == request.roomId &&
u.time_slot_type == timeSlotType &&
u.start_time == startTime &&
u.end_time == endTime,
u => u.id,
OrderByType.Asc
);
if (existingRecord != null)
{
// 更新现有记录为维护类型
existingRecord.type = maintenanceType;
existingRecord.reason = defaultReason;
await _SQRoomUnavailableTimesServices.UpdateAsync(existingRecord);
updateCount++;
}
else
{
// 新增记录
var newRecord = new SQRoomUnavailableTimes
{
room_id = request.roomId,
start_time = startTime,
end_time = endTime,
time_slot_type = timeSlotType,
type = maintenanceType,
reason = defaultReason,
created_at = DateTime.Now
};
await _SQRoomUnavailableTimesServices.InsertAsync(newRecord);
insertCount++;
}
successCount++;
}
jm.code = 0;
jm.msg = $"禁用成功,共设置 {successCount} 个时段(新增 {insertCount} 条,更新 {updateCount} 条)";
}
catch (Exception ex)
{
jm.msg = "禁用失败:" + ex.Message;
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "设置房间整天不可用", "SetRoomUnavailableAllDay", ex);
}
return jm;
}
#endregion
#region ============================================================
// GET: Api/SQRoomPricing/GetRoomUnavailableList
/// <summary>
/// 获取房间某日期的禁用列表
/// </summary>
/// <param name="roomId">房间ID</param>
/// <param name="date">日期</param>
/// <returns></returns>
[HttpGet]
[Description("获取房间禁用列表")]
public async Task<AdminUiCallBack> GetRoomUnavailableList([FromQuery] int roomId, [FromQuery] string date)
{
var jm = new AdminUiCallBack();
try
{
if (roomId <= 0)
{
jm.msg = "请选择房间";
return jm;
}
if (string.IsNullOrEmpty(date))
{
jm.msg = "请选择日期";
return jm;
}
if (!DateTime.TryParse(date, out DateTime dateValue))
{
jm.msg = "日期格式不正确";
return jm;
}
// 获取该日期的开始和结束时间
var startOfDay = dateValue.Date;
var endOfDay = dateValue.Date.AddDays(1);
// 查询该房间该日期的所有禁用记录
var list = await _SQRoomUnavailableTimesServices.QueryListByClauseAsync(
u => u.room_id == roomId &&
u.start_time >= startOfDay &&
u.start_time < endOfDay,
u => u.time_slot_type,
OrderByType.Asc
);
// 时段名称映射
var slotNames = new[] { "凌晨", "上午", "下午", "晚上" };
var typeNames = new Dictionary<int, string>
{
{ 1, "禁用/维护" },
{ 2, "后台预约" },
{ 3, "线下预约" }
};
var result = list.Select(item => new
{
item.id,
item.room_id,
item.time_slot_type,
slot_name = item.time_slot_type.HasValue && item.time_slot_type.Value >= 0 && item.time_slot_type.Value <= 3
? slotNames[item.time_slot_type.Value]
: "未知",
item.type,
type_name = typeNames.ContainsKey(item.type) ? typeNames[item.type] : "未知",
item.reason,
start_time = item.start_time.ToString("yyyy-MM-dd HH:mm:ss"),
end_time = item.end_time.ToString("yyyy-MM-dd HH:mm:ss"),
created_at = item.created_at.ToString("yyyy-MM-dd HH:mm:ss")
}).ToList();
jm.code = 0;
jm.msg = "获取成功";
jm.data = result;
}
catch (Exception ex)
{
jm.msg = "获取失败:" + ex.Message;
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "获取房间禁用列表", "GetRoomUnavailableList", ex);
}
return jm;
}
#endregion
}
#region
/// <summary>
/// 设置房间不可用请求参数
/// </summary>
public class SetRoomUnavailableRequest
{
/// <summary>
/// 房间ID
/// </summary>
public int roomId { get; set; }
/// <summary>
/// 日期 (YYYY-MM-DD)
/// </summary>
public string date { get; set; }
/// <summary>
/// 时段类型0=凌晨1=上午2=下午3=晚上)
/// </summary>
public int timeSlotType { get; set; }
/// <summary>
/// 不可用类型1=禁用/维护2=后台预约3=线下预约)
/// </summary>
public int type { get; set; }
/// <summary>
/// 原因/备注
/// </summary>
public string reason { get; set; }
}
/// <summary>
/// 取消房间不可用请求参数
/// </summary>
public class CancelRoomUnavailableRequest
{
/// <summary>
/// 不可用记录ID数组
/// </summary>
public int[] ids { get; set; }
}
/// <summary>
/// 设置房间整天不可用请求参数
/// </summary>
public class SetRoomUnavailableAllDayRequest
{
/// <summary>
/// 房间ID
/// </summary>
public int roomId { get; set; }
/// <summary>
/// 日期 (YYYY-MM-DD)
/// </summary>
public string date { get; set; }
}
/// <summary>
/// 取消房间整天不可用请求参数
/// </summary>
public class CancelRoomUnavailableAllDayRequest
{
/// <summary>
/// 房间ID
/// </summary>
public int roomId { get; set; }
/// <summary>
/// 日期 (YYYY-MM-DD)
/// </summary>
public string date { get; set; }
}
#endregion
}

View File

@ -4013,7 +4013,7 @@
</summary>
</member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.#ctor(Microsoft.AspNetCore.Hosting.IWebHostEnvironment,CoreCms.Net.IServices.ISQRoomPricingServices,CoreCms.Net.IServices.ISQRoomsServices,CoreCms.Net.IServices.ISQReservationsServices,CoreCms.Net.IServices.ISQReservationParticipantsServices,CoreCms.Net.IServices.ICoreCmsUserServices,AutoMapper.IMapper)">
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.#ctor(Microsoft.AspNetCore.Hosting.IWebHostEnvironment,CoreCms.Net.IServices.ISQRoomPricingServices,CoreCms.Net.IServices.ISQRoomsServices,CoreCms.Net.IServices.ISQReservationsServices,CoreCms.Net.IServices.ISQReservationParticipantsServices,CoreCms.Net.IServices.ISQRoomUnavailableTimesServices,CoreCms.Net.IServices.ICoreCmsUserServices,AutoMapper.IMapper)">
<summary>
构造函数
</summary>
@ -4120,6 +4120,123 @@
根据时间判断时段类型
</summary>
</member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.SetRoomUnavailable(CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableRequest)">
<summary>
设置房间时段不可用
</summary>
<param name="roomId">房间ID</param>
<param name="date">日期</param>
<param name="timeSlotType">时段类型0=凌晨1=上午2=下午3=晚上)</param>
<param name="type">不可用类型1=禁用/维护2=后台预约3=线下预约)</param>
<param name="reason">原因/备注</param>
<returns></returns>
</member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.GetTimeSlotRange(System.DateTime,System.Int32)">
<summary>
根据时段类型获取时间范围
</summary>
</member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.CancelRoomUnavailable(CoreCms.Net.Web.Admin.Controllers.CancelRoomUnavailableRequest)">
<summary>
取消房间时段不可用
</summary>
<param name="id">不可用记录ID</param>
<returns></returns>
</member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.CancelRoomUnavailableAllDay(CoreCms.Net.Web.Admin.Controllers.CancelRoomUnavailableAllDayRequest)">
<summary>
取消房间整天不可用(删除该日期的所有禁用记录)
</summary>
<param name="roomId">房间ID</param>
<param name="date">日期</param>
<returns></returns>
</member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.SetRoomUnavailableAllDay(CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableAllDayRequest)">
<summary>
设置房间整天不可用(禁用所有时段,类型为维护)
</summary>
<param name="roomId">房间ID</param>
<param name="date">日期</param>
<returns></returns>
</member>
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.GetRoomUnavailableList(System.Int32,System.String)">
<summary>
获取房间某日期的禁用列表
</summary>
<param name="roomId">房间ID</param>
<param name="date">日期</param>
<returns></returns>
</member>
<member name="T:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableRequest">
<summary>
设置房间不可用请求参数
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableRequest.roomId">
<summary>
房间ID
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableRequest.date">
<summary>
日期 (YYYY-MM-DD)
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableRequest.timeSlotType">
<summary>
时段类型0=凌晨1=上午2=下午3=晚上)
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableRequest.type">
<summary>
不可用类型1=禁用/维护2=后台预约3=线下预约)
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableRequest.reason">
<summary>
原因/备注
</summary>
</member>
<member name="T:CoreCms.Net.Web.Admin.Controllers.CancelRoomUnavailableRequest">
<summary>
取消房间不可用请求参数
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.CancelRoomUnavailableRequest.ids">
<summary>
不可用记录ID数组
</summary>
</member>
<member name="T:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableAllDayRequest">
<summary>
设置房间整天不可用请求参数
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableAllDayRequest.roomId">
<summary>
房间ID
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.SetRoomUnavailableAllDayRequest.date">
<summary>
日期 (YYYY-MM-DD)
</summary>
</member>
<member name="T:CoreCms.Net.Web.Admin.Controllers.CancelRoomUnavailableAllDayRequest">
<summary>
取消房间整天不可用请求参数
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.CancelRoomUnavailableAllDayRequest.roomId">
<summary>
房间ID
</summary>
</member>
<member name="P:CoreCms.Net.Web.Admin.Controllers.CancelRoomUnavailableAllDayRequest.date">
<summary>
日期 (YYYY-MM-DD)
</summary>
</member>
<member name="T:CoreCms.Net.Web.Admin.Controllers.SQRoomsController">
<summary>
房间表

View File

@ -0,0 +1,351 @@
<script type="text/html" template lay-done="layui.data.sendParams(d);">
<div class="layui-form" lay-filter="setUnavailableForm" style="padding: 20px;">
<!-- 日期和房间合并一行 -->
<div class="layui-form-item">
<label class="layui-form-label">日期</label>
<div class="layui-input-inline" style="width: 200px;">
<input type="text" class="layui-input" value="{{d.params.data.dateStr || ''}}" readonly>
</div>
<label class="layui-form-label" style="margin-left: 20px;">房间</label>
<div class="layui-input-inline" style="width: 200px;">
<input type="text" class="layui-input" value="{{d.params.data.roomName || ''}}" readonly>
</div>
</div>
<!-- 时段列表 -->
<div class="time-slot-list" style="margin-top: 20px;">
<!-- 凌晨时段 -->
<div class="time-slot-item" data-slot="0" style="border: 1px solid #e6e6e6; border-radius: 4px; padding: 15px; margin-bottom: 15px; background: #fff;">
<div class="layui-form-item" style="margin-bottom: 10px;">
<label class="layui-form-label" style="width: 80px;">凌晨:</label>
<div class="layui-input-block" style="margin-left: 90px; display: flex; align-items: center; flex-wrap: wrap;">
<input type="checkbox" name="switch_0" lay-skin="switch" lay-text="开启|关闭" lay-filter="slot-switch">
<input type="hidden" name="slot_id_0" value="">
<div style="margin-left: 20px; flex: 1; min-width: 200px;">
<div style="display: inline-block; margin-right: 20px;">
<input type="radio" name="type_0" value="1" title="禁用/维护" checked disabled>
<input type="radio" name="type_0" value="2" title="后台预约" disabled>
<input type="radio" name="type_0" value="3" title="线下预约" disabled>
</div>
<div style="display: inline-block; width: 300px;">
<input type="text" name="reason_0" placeholder="请输入原因/备注" class="layui-input" disabled>
</div>
</div>
</div>
</div>
</div>
<!-- 上午时段 -->
<div class="time-slot-item" data-slot="1" style="border: 1px solid #e6e6e6; border-radius: 4px; padding: 15px; margin-bottom: 15px; background: #fff;">
<div class="layui-form-item" style="margin-bottom: 10px;">
<label class="layui-form-label" style="width: 80px;">上午:</label>
<div class="layui-input-block" style="margin-left: 90px; display: flex; align-items: center; flex-wrap: wrap;">
<input type="checkbox" name="switch_1" lay-skin="switch" lay-text="开启|关闭" lay-filter="slot-switch">
<input type="hidden" name="slot_id_1" value="">
<div style="margin-left: 20px; flex: 1; min-width: 200px;">
<div style="display: inline-block; margin-right: 20px;">
<input type="radio" name="type_1" value="1" title="禁用/维护" checked disabled>
<input type="radio" name="type_1" value="2" title="后台预约" disabled>
<input type="radio" name="type_1" value="3" title="线下预约" disabled>
</div>
<div style="display: inline-block; width: 300px;">
<input type="text" name="reason_1" placeholder="请输入原因/备注" class="layui-input" disabled>
</div>
</div>
</div>
</div>
</div>
<!-- 下午时段 -->
<div class="time-slot-item" data-slot="2" style="border: 1px solid #e6e6e6; border-radius: 4px; padding: 15px; margin-bottom: 15px; background: #fff;">
<div class="layui-form-item" style="margin-bottom: 10px;">
<label class="layui-form-label" style="width: 80px;">下午:</label>
<div class="layui-input-block" style="margin-left: 90px; display: flex; align-items: center; flex-wrap: wrap;">
<input type="checkbox" name="switch_2" lay-skin="switch" lay-text="开启|关闭" lay-filter="slot-switch">
<input type="hidden" name="slot_id_2" value="">
<div style="margin-left: 20px; flex: 1; min-width: 200px;">
<div style="display: inline-block; margin-right: 20px;">
<input type="radio" name="type_2" value="1" title="禁用/维护" checked disabled>
<input type="radio" name="type_2" value="2" title="后台预约" disabled>
<input type="radio" name="type_2" value="3" title="线下预约" disabled>
</div>
<div style="display: inline-block; width: 300px;">
<input type="text" name="reason_2" placeholder="请输入原因/备注" class="layui-input" disabled>
</div>
</div>
</div>
</div>
</div>
<!-- 晚上时段 -->
<div class="time-slot-item" data-slot="3" style="border: 1px solid #e6e6e6; border-radius: 4px; padding: 15px; margin-bottom: 15px; background: #fff;">
<div class="layui-form-item" style="margin-bottom: 10px;">
<label class="layui-form-label" style="width: 80px;">晚上:</label>
<div class="layui-input-block" style="margin-left: 90px; display: flex; align-items: center; flex-wrap: wrap;">
<input type="checkbox" name="switch_3" lay-skin="switch" lay-text="开启|关闭" lay-filter="slot-switch">
<input type="hidden" name="slot_id_3" value="">
<div style="margin-left: 20px; flex: 1; min-width: 200px;">
<div style="display: inline-block; margin-right: 20px;">
<input type="radio" name="type_3" value="1" title="禁用/维护" checked disabled>
<input type="radio" name="type_3" value="2" title="后台预约" disabled>
<input type="radio" name="type_3" value="3" title="线下预约" disabled>
</div>
<div style="display: inline-block; width: 300px;">
<input type="text" name="reason_3" placeholder="请输入原因/备注" class="layui-input" disabled>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</script>
<script>
var debug = layui.setter.debug;
layui.data.sendParams = function (d) {
//开启调试情况下获取接口赋值数据
if (debug) { console.log(d.params.data); }
layui.use(['admin', 'form', 'element', 'coreHelper', 'layer'],
function () {
var $ = layui.$
, form = layui.form
, element = layui.element
, admin = layui.admin
, coreHelper = layui.coreHelper
, layer = layui.layer;
var params = d.params.data;
var dateStr = params.dateStr;
var selectedRoomId = params.selectedRoomId;
var renderCalendar = params.renderCalendar;
var refreshSingleDay = params.refreshSingleDay; // 刷新单个日期的函数
// 存储已禁用的记录ID用于取消禁用
var unavailableRecords = {};
// 存储原始数据,用于对比是否有变化
var originalData = {};
// 切换时段表单元素的启用/禁用状态
function toggleSlotForm(slotType, enabled) {
var slotItem = $('.time-slot-item[data-slot="' + slotType + '"]');
var typeRadios = slotItem.find('input[name="type_' + slotType + '"]');
var reasonInput = slotItem.find('input[name="reason_' + slotType + '"]');
if (enabled) {
typeRadios.prop('disabled', false);
reasonInput.prop('disabled', false);
} else {
typeRadios.prop('disabled', true);
reasonInput.prop('disabled', true);
}
form.render('radio', 'setUnavailableForm');
}
// 更新时段状态显示
function updateSlotStatus(slotType, item) {
var slotItem = $('.time-slot-item[data-slot="' + slotType + '"]');
var switchInput = slotItem.find('input[name="switch_' + slotType + '"]');
if (item) {
// 已禁用,打开开关
switchInput.prop('checked', true);
// 保存记录ID
slotItem.find('input[name="slot_id_' + slotType + '"]').val(item.id);
unavailableRecords[slotType] = item.id;
// 保存原始数据用于对比
originalData[slotType] = {
type: item.type,
reason: item.reason || '',
enabled: true
};
// 启用表单元素
toggleSlotForm(slotType, true);
} else {
// 未禁用,关闭开关
switchInput.prop('checked', false);
slotItem.find('input[name="slot_id_' + slotType + '"]').val('');
delete unavailableRecords[slotType];
originalData[slotType] = {
type: null,
reason: '',
enabled: false
};
// 禁用表单元素
toggleSlotForm(slotType, false);
}
form.render('checkbox', 'setUnavailableForm');
}
// 开关切换事件
form.on('switch(slot-switch)', function(data) {
var slotType = parseInt(data.elem.name.replace('switch_', ''));
var isChecked = data.elem.checked;
var slotItem = $('.time-slot-item[data-slot="' + slotType + '"]');
toggleSlotForm(slotType, isChecked);
// 如果关闭开关,清空类型和原因
if (!isChecked) {
slotItem.find('input[name="type_' + slotType + '"][value="1"]').prop('checked', true);
slotItem.find('input[name="reason_' + slotType + '"]').val('');
form.render('radio', 'setUnavailableForm');
}
});
// 加载该日期的禁用记录
coreHelper.Get("Api/SQRoomPricing/GetRoomUnavailableList?roomId=" + selectedRoomId + "&date=" + encodeURIComponent(dateStr), function(e) {
if (e.code === 0 && e.data) {
// 初始化所有时段为未禁用状态
for (var i = 0; i <= 3; i++) {
updateSlotStatus(i, null);
}
// 遍历禁用记录,填充到对应的时段
if (e.data && e.data.length > 0) {
layui.each(e.data, function(index, item) {
var slotType = item.time_slot_type;
if (slotType !== null && slotType !== undefined && slotType >= 0 && slotType <= 3) {
// 设置类型单选框
var typeRadio = $('input[name="type_' + slotType + '"][value="' + item.type + '"]');
if (typeRadio.length > 0) {
typeRadio.prop('checked', true);
}
// 设置原因
var reasonInput = $('input[name="reason_' + slotType + '"]');
if (reasonInput.length > 0) {
reasonInput.val(item.reason || '');
}
// 更新状态显示
updateSlotStatus(slotType, item);
}
});
}
}
//重载form
form.render('radio', 'setUnavailableForm');
form.render('checkbox', 'setUnavailableForm');
});
// 保存提交处理函数到全局,供弹窗的确定按钮调用
window._setUnavailableSubmitHandler = function(layero, index) {
var saveTasks = [];
var cancelTasks = [];
var hasChanges = false;
// 遍历所有4个时段收集需要保存或取消的数据
for (var slotType = 0; slotType <= 3; slotType++) {
var switchChecked = layero.find('input[name="switch_' + slotType + '"]').prop('checked');
var slotId = layero.find('input[name="slot_id_' + slotType + '"]').val();
var original = originalData[slotType] || { type: null, reason: '', enabled: false };
if (switchChecked) {
// 开关开启,需要设置禁用
var type = parseInt(layero.find('input[name="type_' + slotType + '"]:checked').val());
var reason = (layero.find('input[name="reason_' + slotType + '"]').val() || '').trim();
// 判断是否需要保存(新增或修改)
var needSave = false;
if (slotId) {
// 已禁用状态,检查是否有修改
if (original.type !== type || original.reason !== reason) {
needSave = true;
}
} else {
// 未禁用状态,需要新增
needSave = true;
}
if (needSave) {
hasChanges = true;
saveTasks.push({
slotType: slotType,
type: type,
reason: reason
});
}
} else {
// 开关关闭,需要取消禁用
if (slotId) {
hasChanges = true;
cancelTasks.push({
slotType: slotType,
slotId: parseInt(slotId)
});
}
}
}
if (!hasChanges) {
layer.msg('没有需要保存的更改');
return;
}
// 显示加载提示
var loadIndex = layer.load(1, {shade: [0.1,'#fff']});
var completedCount = 0;
var totalTasks = saveTasks.length + cancelTasks.length;
var hasError = false;
var errorMsg = '';
// 依次执行保存和取消任务
function executeNext() {
if (completedCount >= totalTasks) {
layer.close(loadIndex);
if (hasError) {
layer.msg(errorMsg || '部分操作失败');
} else {
layer.close(index);
layer.msg('保存成功');
// 优先使用刷新单个日期,如果没有则刷新整个日历
if (refreshSingleDay && typeof refreshSingleDay === 'function') {
refreshSingleDay(dateStr); // 只刷新当前日期
} else if (renderCalendar && typeof renderCalendar === 'function') {
renderCalendar(); // 刷新整个日历(兼容旧逻辑)
}
}
return;
}
// 先执行取消任务,再执行保存任务
if (completedCount < cancelTasks.length) {
var cancelTask = cancelTasks[completedCount];
coreHelper.Post("Api/SQRoomPricing/CancelRoomUnavailable", {
ids: [cancelTask.slotId]
}, function(e) {
completedCount++;
if (e.code !== 0) {
hasError = true;
errorMsg = e.msg || '取消失败';
}
executeNext();
});
} else {
var saveTask = saveTasks[completedCount - cancelTasks.length];
coreHelper.Post("Api/SQRoomPricing/SetRoomUnavailable", {
roomId: selectedRoomId,
date: dateStr,
timeSlotType: saveTask.slotType,
type: saveTask.type,
reason: saveTask.reason
}, function(e) {
completedCount++;
if (e.code !== 0) {
hasError = true;
errorMsg = e.msg || '保存失败';
}
executeNext();
});
}
}
// 开始执行
executeNext();
};
})
};
</script>