321
This commit is contained in:
parent
3857ecad81
commit
0173638400
|
|
@ -17,6 +17,7 @@ using CoreCms.Net.Model.Entities;
|
|||
using CoreCms.Net.Model.Entities.Expression;
|
||||
using CoreCms.Net.Model.FromBody;
|
||||
using CoreCms.Net.Model.ViewModels.UI;
|
||||
using CoreCms.Net.Model.ViewModels.SQ;
|
||||
using CoreCms.Net.Utility.Extensions;
|
||||
using CoreCms.Net.Utility.Helper;
|
||||
using CoreCms.Net.Web.Admin.Infrastructure;
|
||||
|
|
@ -30,11 +31,14 @@ using NPOI.HSSF.UserModel;
|
|||
using SqlSugar;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using AutoMapper;
|
||||
|
||||
namespace CoreCms.Net.Web.Admin.Controllers
|
||||
{
|
||||
|
|
@ -50,16 +54,31 @@ namespace CoreCms.Net.Web.Admin.Controllers
|
|||
{
|
||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||
private readonly ISQRoomPricingServices _SQRoomPricingServices;
|
||||
private readonly ISQRoomsServices _SQRoomsServices;
|
||||
private readonly ISQReservationsServices _SQReservationsServices;
|
||||
private readonly ISQReservationParticipantsServices _SQReservationParticipantsServices;
|
||||
private readonly ICoreCmsUserServices _userServices;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
///</summary>
|
||||
public SQRoomPricingController(IWebHostEnvironment webHostEnvironment
|
||||
, ISQRoomPricingServices SQRoomPricingServices
|
||||
, ISQRoomsServices SQRoomsServices
|
||||
, ISQReservationsServices SQReservationsServices
|
||||
, ISQReservationParticipantsServices SQReservationParticipantsServices
|
||||
, ICoreCmsUserServices userServices
|
||||
, IMapper mapper
|
||||
)
|
||||
{
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
_SQRoomPricingServices = SQRoomPricingServices;
|
||||
_SQRoomsServices = SQRoomsServices;
|
||||
_SQReservationsServices = SQReservationsServices;
|
||||
_SQReservationParticipantsServices = SQReservationParticipantsServices;
|
||||
_userServices = userServices;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
#region 获取列表============================================================
|
||||
|
|
@ -820,6 +839,265 @@ namespace CoreCms.Net.Web.Admin.Controllers
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region 获取房间时段状态============================================================
|
||||
// GET: Api/SQRoomPricing/GetRoomListWithSlots
|
||||
/// <summary>
|
||||
/// 获取房间列表及时段状态(用于日历视图)
|
||||
/// </summary>
|
||||
/// <param name="date">日期</param>
|
||||
/// <param name="showOnlyAvailable">是否只显示可用房间</param>
|
||||
/// <param name="currentTimeSlot">当前时段</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
[Description("获取房间列表及时段状态")]
|
||||
public async Task<AdminUiCallBack> GetRoomListWithSlots([FromQuery] string date, [FromQuery] bool showOnlyAvailable = false, [FromQuery] int? currentTimeSlot = null)
|
||||
{
|
||||
var jm = new AdminUiCallBack();
|
||||
|
||||
// 验证日期参数
|
||||
if (string.IsNullOrEmpty(date))
|
||||
{
|
||||
jm.msg = "请提供日期参数";
|
||||
return jm;
|
||||
}
|
||||
|
||||
if (!DateTime.TryParse(date, out DateTime dateValue))
|
||||
{
|
||||
jm.msg = "日期格式不正确";
|
||||
return jm;
|
||||
}
|
||||
|
||||
// 调用服务获取数据
|
||||
var result = await _SQRoomsServices.GetRoomListWithSlotsAsync(dateValue, showOnlyAvailable, currentTimeSlot);
|
||||
|
||||
jm.code = 0;
|
||||
jm.msg = "获取成功";
|
||||
jm.data = result;
|
||||
|
||||
return jm;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 获取日期详情============================================================
|
||||
// GET: Api/SQRoomPricing/GetDateDetails
|
||||
/// <summary>
|
||||
/// 获取指定日期和房间的详细信息(用于日历视图点击日期)
|
||||
/// </summary>
|
||||
/// <param name="date">日期</param>
|
||||
/// <param name="roomId">房间ID</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
[Description("获取日期详情")]
|
||||
public async Task<AdminUiCallBack> GetDateDetails([FromQuery] string date, [FromQuery] int roomId)
|
||||
{
|
||||
var jm = new AdminUiCallBack();
|
||||
|
||||
try
|
||||
{
|
||||
// 验证参数
|
||||
if (string.IsNullOrEmpty(date))
|
||||
{
|
||||
jm.msg = "请提供日期参数";
|
||||
return jm;
|
||||
}
|
||||
|
||||
if (roomId <= 0)
|
||||
{
|
||||
jm.msg = "请提供房间ID";
|
||||
return jm;
|
||||
}
|
||||
|
||||
if (!DateTime.TryParse(date, out DateTime dateValue))
|
||||
{
|
||||
jm.msg = "日期格式不正确";
|
||||
return jm;
|
||||
}
|
||||
|
||||
// 获取房间信息
|
||||
var room = await _SQRoomsServices.QueryByIdAsync(roomId);
|
||||
if (room == null)
|
||||
{
|
||||
jm.msg = "房间不存在";
|
||||
return jm;
|
||||
}
|
||||
|
||||
// 获取该日期该房间的时段状态
|
||||
var roomListWithSlots = await _SQRoomsServices.GetRoomListWithSlotsAsync(dateValue, false, null);
|
||||
var roomData = roomListWithSlots?.FirstOrDefault(r => r.id == roomId);
|
||||
|
||||
// 获取该日期该房间的预约列表
|
||||
var startTime = dateValue.Date;
|
||||
var endTime = dateValue.Date.AddDays(1);
|
||||
var reservations = await _SQReservationsServices.QueryListByClauseAsync(
|
||||
p => p.room_id == roomId &&
|
||||
p.start_time >= startTime &&
|
||||
p.start_time < endTime &&
|
||||
p.status != 4, // 排除已取消的预约
|
||||
p => p.start_time,
|
||||
OrderByType.Asc
|
||||
);
|
||||
|
||||
// 查询所有预约的参与者
|
||||
var reservationIds = reservations.Select(r => r.id).ToList();
|
||||
List<SQReservationParticipants> allParticipants = new List<SQReservationParticipants>();
|
||||
List<CoreCmsUser> userList = new List<CoreCmsUser>();
|
||||
|
||||
if (reservationIds.Any())
|
||||
{
|
||||
allParticipants = await _SQReservationParticipantsServices.QueryListByClauseAsync(
|
||||
p => reservationIds.Contains(p.reservation_id),
|
||||
p => p.role,
|
||||
OrderByType.Desc,
|
||||
true
|
||||
);
|
||||
|
||||
// 查询所有参与者的用户信息
|
||||
var userIds = allParticipants.Select(p => p.user_id).Distinct().ToList();
|
||||
if (userIds.Any())
|
||||
{
|
||||
userList = await _userServices.QueryListByClauseAsync(
|
||||
u => userIds.Contains(u.id),
|
||||
u => u.id,
|
||||
OrderByType.Asc,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理时段数据,添加参与者头像和数量
|
||||
var enhancedTimeSlots = new List<object>();
|
||||
if (roomData?.time_slots != null)
|
||||
{
|
||||
foreach (var slot in roomData.time_slots)
|
||||
{
|
||||
// 获取该时段的所有预约
|
||||
var slotReservations = reservations.Where(r =>
|
||||
r.time_slot_type.HasValue && r.time_slot_type.Value == slot.slot_type
|
||||
).ToList();
|
||||
|
||||
// 获取该时段所有预约的参与者(正常状态)
|
||||
var slotReservationIds = slotReservations.Select(r => r.id).ToList();
|
||||
var slotParticipants = allParticipants.Where(p =>
|
||||
slotReservationIds.Contains(p.reservation_id) && p.status == 0
|
||||
).ToList();
|
||||
|
||||
// 提取头像列表
|
||||
var avatars = slotParticipants
|
||||
.Select(p => userList.FirstOrDefault(u => u.id == p.user_id)?.avatarImage)
|
||||
.Where(avatar => !string.IsNullOrEmpty(avatar))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
enhancedTimeSlots.Add(new
|
||||
{
|
||||
slot_type = slot.slot_type,
|
||||
slot_name = slot.slot_name,
|
||||
status = slot.status,
|
||||
price_desc_standard = slot.price_desc_standard,
|
||||
price_desc_member = slot.price_desc_member,
|
||||
reservation_count = slotParticipants.Count,
|
||||
participant_avatars = avatars
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按时段分组预约
|
||||
var reservationsBySlot = new Dictionary<int, List<object>>();
|
||||
for (int i = 0; i <= 3; i++)
|
||||
{
|
||||
reservationsBySlot[i] = new List<object>();
|
||||
}
|
||||
|
||||
foreach (var reservation in reservations)
|
||||
{
|
||||
// 确定预约所属时段
|
||||
int slotType = reservation.time_slot_type ?? GetTimeSlotTypeFromTime(reservation.start_time);
|
||||
|
||||
// 获取该预约的参与者
|
||||
var participants = allParticipants.Where(p => p.reservation_id == reservation.id)
|
||||
.OrderBy(p => p.role == 1 ? 0 : 1) // 发起者优先
|
||||
.ThenBy(p => p.status) // 正常状态优先
|
||||
.ToList();
|
||||
|
||||
var participantsDto = _mapper.Map<List<SQReservationParticipantsDto>>(participants);
|
||||
|
||||
// 填充用户信息
|
||||
foreach (var p in participantsDto)
|
||||
{
|
||||
var user = userList.FirstOrDefault(u => u.id == p.user_id);
|
||||
if (user != null)
|
||||
{
|
||||
p.UserName = user.nickName;
|
||||
p.AvatarImage = user.avatarImage;
|
||||
}
|
||||
}
|
||||
|
||||
reservationsBySlot[slotType].Add(new
|
||||
{
|
||||
id = reservation.id,
|
||||
title = reservation.title,
|
||||
player_count = reservation.player_count,
|
||||
deposit_fee = reservation.deposit_fee,
|
||||
latest_arrival_time = reservation.latest_arrival_time?.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
game_type = reservation.game_type,
|
||||
game_rule = reservation.game_rule,
|
||||
start_time = reservation.start_time.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
end_time = reservation.end_time.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
status = reservation.status,
|
||||
participants = participantsDto.Select(p => new
|
||||
{
|
||||
id = p.id,
|
||||
user_id = p.user_id,
|
||||
user_name = p.UserName,
|
||||
avatar_image = p.AvatarImage,
|
||||
join_time = p.join_time.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
role = p.role,
|
||||
quit_time = p.quit_time?.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
status = p.status,
|
||||
is_refund = p.is_refund,
|
||||
remarks = p.remarks
|
||||
}).ToList()
|
||||
});
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
var result = new
|
||||
{
|
||||
date = date,
|
||||
room_id = roomId,
|
||||
room_name = room.name,
|
||||
time_slots = enhancedTimeSlots,
|
||||
reservations_by_slot = reservationsBySlot
|
||||
};
|
||||
|
||||
jm.code = 0;
|
||||
jm.msg = "获取成功";
|
||||
jm.data = result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
jm.msg = "获取数据时发生错误:" + ex.Message;
|
||||
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "获取日期详情", "GetDateDetails", ex);
|
||||
}
|
||||
|
||||
return jm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据时间判断时段类型
|
||||
/// </summary>
|
||||
private int GetTimeSlotTypeFromTime(DateTime time)
|
||||
{
|
||||
var hour = time.Hour;
|
||||
if (hour >= 0 && hour < 6) return 0; // 凌晨 00:00-06:00
|
||||
if (hour >= 6 && hour < 12) return 1; // 上午 06:00-12:00
|
||||
if (hour >= 12 && hour < 18) return 2; // 下午 12:00-18:00
|
||||
return 3; // 晚上 18:00-24:00
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4013,7 +4013,7 @@
|
|||
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.#ctor(Microsoft.AspNetCore.Hosting.IWebHostEnvironment,CoreCms.Net.IServices.ISQRoomPricingServices)">
|
||||
<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)">
|
||||
<summary>
|
||||
构造函数
|
||||
</summary>
|
||||
|
|
@ -4098,6 +4098,28 @@
|
|||
<param name="entity"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.GetRoomListWithSlots(System.String,System.Boolean,System.Nullable{System.Int32})">
|
||||
<summary>
|
||||
获取房间列表及时段状态(用于日历视图)
|
||||
</summary>
|
||||
<param name="date">日期</param>
|
||||
<param name="showOnlyAvailable">是否只显示可用房间</param>
|
||||
<param name="currentTimeSlot">当前时段</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.GetDateDetails(System.String,System.Int32)">
|
||||
<summary>
|
||||
获取指定日期和房间的详细信息(用于日历视图点击日期)
|
||||
</summary>
|
||||
<param name="date">日期</param>
|
||||
<param name="roomId">房间ID</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CoreCms.Net.Web.Admin.Controllers.SQRoomPricingController.GetTimeSlotTypeFromTime(System.DateTime)">
|
||||
<summary>
|
||||
根据时间判断时段类型
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:CoreCms.Net.Web.Admin.Controllers.SQRoomsController">
|
||||
<summary>
|
||||
房间表
|
||||
|
|
|
|||
|
|
@ -749,6 +749,8 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//执行单个删除
|
||||
function doDelete(obj) {
|
||||
coreHelper.Post("Api/SQReservations/DoDelete", { id: obj.data.id }, function (e) {
|
||||
|
|
|
|||
|
|
@ -9,30 +9,183 @@
|
|||
<!--当前位置结束-->
|
||||
<style>
|
||||
/* 重写样式 */
|
||||
.calendar-view {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.calendar-header h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendar-nav button {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.calendar-weekday {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
min-height: 140px;
|
||||
background: #fff;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.calendar-day:hover {
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.calendar-day.empty {
|
||||
background: #fafafa;
|
||||
border-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.calendar-day.today {
|
||||
border-color: #009688;
|
||||
background: #f0fffe;
|
||||
}
|
||||
|
||||
.calendar-day-header {
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.calendar-day-price {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.calendar-day-member {
|
||||
display: inline-block;
|
||||
background: #ff5722;
|
||||
color: #fff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.calendar-day-slots {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.calendar-slot {
|
||||
font-size: 11px;
|
||||
padding: 3px 5px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendar-slot.available {
|
||||
background: #e8f5e9;
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.calendar-slot.unavailable {
|
||||
background: #ffebee;
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
.calendar-slot.reserved {
|
||||
background: #fff3e0;
|
||||
color: #ff9800;
|
||||
}
|
||||
|
||||
.calendar-slot.using {
|
||||
background: #a3a9ac1e;
|
||||
color: #ff9800;
|
||||
}
|
||||
</style>
|
||||
<script type="text/html" template lay-type="Post" lay-url="Api/SQRoomPricing/GetIndex" lay-done="layui.data.done(d);">
|
||||
|
||||
</script>
|
||||
<div class="table-body">
|
||||
<table id="LAY-app-SQRoomPricing-tableBox" lay-filter="LAY-app-SQRoomPricing-tableBox"></table>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="LAY-app-SQRoomPricing-toolbar">
|
||||
<div class="layui-form coreshop-toolbar-search-form">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label" for="room_id">房间号</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="room_id" id="search_room_id">
|
||||
<option value="">请选择房间号</option>
|
||||
</select>
|
||||
<!-- 独立的搜索工具栏 -->
|
||||
<div class="layui-card" style="margin-bottom: 10px;">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-form coreshop-toolbar-search-form">
|
||||
<div class="layui-form-item" style="margin-bottom: 0;">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label" for="room_id">房间号</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="room_id" id="search_room_id" lay-filter="search_room_id">
|
||||
<option value="">请选择房间号</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline" id="table-view-search-btn">
|
||||
<button class="layui-btn layui-btn-sm" lay-submit lay-filter="LAY-app-SQRoomPricing-search"><i class="layui-icon layui-icon-search"></i>筛选</button>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" id="toggleViewBtn" type="button">
|
||||
<i class="layui-icon layui-icon-date"></i>切换日历视图
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layui-btn-sm" lay-submit lay-filter="LAY-app-SQRoomPricing-search"><i class="layui-icon layui-icon-search"></i>筛选</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-body" id="table-view">
|
||||
<table id="LAY-app-SQRoomPricing-tableBox" lay-filter="LAY-app-SQRoomPricing-tableBox"></table>
|
||||
</div>
|
||||
|
||||
<div class="calendar-view" id="calendar-view">
|
||||
<div class="calendar-header">
|
||||
<div class="calendar-nav">
|
||||
<button class="layui-btn layui-btn-sm" id="prevMonthBtn">
|
||||
<i class="layui-icon layui-icon-left"></i> 上一月
|
||||
</button>
|
||||
</div>
|
||||
<h3 id="currentMonthTitle"></h3>
|
||||
<div class="calendar-nav">
|
||||
<button class="layui-btn layui-btn-sm" id="nextMonthBtn">
|
||||
下一月 <i class="layui-icon layui-icon-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="calendar-grid" id="calendarGrid">
|
||||
<!-- 日历将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="LAY-app-SQRoomPricing-toolbar">
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="LAY-app-SQRoomPricing-pagebar">
|
||||
|
|
@ -66,6 +219,9 @@
|
|||
var indexData;
|
||||
var debug= layui.setter.debug;
|
||||
var roomOptions = []; // 定义在外部作用域,供表格templet使用
|
||||
var currentView = 'table'; // 当前视图:table 或 calendar
|
||||
var currentMonth = new Date(); // 当前显示的月份
|
||||
var selectedRoomId = null; // 选中的房间ID
|
||||
|
||||
layui.data.done = function (d) {
|
||||
//开启调试情况下获取接口赋值数据
|
||||
|
|
@ -117,7 +273,7 @@
|
|||
pagebar: '#LAY-app-SQRoomPricing-pagebar',
|
||||
className: 'pagebarbox',
|
||||
defaultToolbar: ['filter', 'print', 'exports'],
|
||||
height: 'full-127',//面包屑142px,搜索框4行172,3行137,2行102,1行67
|
||||
height: 'full-210',//面包屑142px + 独立搜索栏68px = 210px
|
||||
page: true,
|
||||
limit: 30,
|
||||
limits: [10, 15, 20, 25, 30, 50, 100, 200],
|
||||
|
|
@ -385,10 +541,547 @@
|
|||
});
|
||||
|
||||
|
||||
// 视图切换功能
|
||||
$('#toggleViewBtn').on('click', function() {
|
||||
if (currentView === 'table') {
|
||||
// 切换到日历视图
|
||||
currentView = 'calendar';
|
||||
$('#table-view').hide();
|
||||
$('#calendar-view').show();
|
||||
$('#table-view-search-btn').hide();
|
||||
$(this).html('<i class="layui-icon layui-icon-template"></i>切换表格视图');
|
||||
|
||||
// 获取当前选中的房间ID
|
||||
var currentRoomId = $('#search_room_id').val();
|
||||
|
||||
// 如果没有选择房间,默认选择第一个
|
||||
if (!currentRoomId && roomOptions.length > 0) {
|
||||
selectedRoomId = roomOptions[0].id;
|
||||
$('#search_room_id').val(selectedRoomId);
|
||||
form.render('select');
|
||||
} else {
|
||||
selectedRoomId = currentRoomId ? parseInt(currentRoomId) : null;
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
console.log('切换到日历视图,选中房间ID:', selectedRoomId);
|
||||
}
|
||||
|
||||
renderCalendar();
|
||||
} else {
|
||||
// 切换到表格视图
|
||||
currentView = 'table';
|
||||
$('#table-view').show();
|
||||
$('#calendar-view').hide();
|
||||
$('#table-view-search-btn').show();
|
||||
$(this).html('<i class="layui-icon layui-icon-date"></i>切换日历视图');
|
||||
}
|
||||
});
|
||||
|
||||
// 监听房间选择变化(日历视图)
|
||||
form.on('select(search_room_id)', function(data) {
|
||||
if (currentView === 'calendar') {
|
||||
selectedRoomId = data.value ? parseInt(data.value) : null;
|
||||
if (debug) {
|
||||
console.log('房间选择变化,新房间ID:', selectedRoomId);
|
||||
}
|
||||
if (selectedRoomId) {
|
||||
renderCalendar();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 上一月按钮
|
||||
$('#prevMonthBtn').on('click', function() {
|
||||
currentMonth.setMonth(currentMonth.getMonth() - 1);
|
||||
renderCalendar();
|
||||
});
|
||||
|
||||
// 下一月按钮
|
||||
$('#nextMonthBtn').on('click', function() {
|
||||
currentMonth.setMonth(currentMonth.getMonth() + 1);
|
||||
renderCalendar();
|
||||
});
|
||||
|
||||
// 渲染日历
|
||||
function renderCalendar() {
|
||||
if (!selectedRoomId) {
|
||||
layer.msg('请先选择房间');
|
||||
return;
|
||||
}
|
||||
|
||||
var year = currentMonth.getFullYear();
|
||||
var month = currentMonth.getMonth();
|
||||
|
||||
// 更新标题
|
||||
$('#currentMonthTitle').text(year + '年 ' + (month + 1) + '月');
|
||||
|
||||
// 获取当月第一天和最后一天
|
||||
var firstDay = new Date(year, month, 1);
|
||||
var lastDay = new Date(year, month + 1, 0);
|
||||
var firstDayOfWeek = firstDay.getDay() || 7; // 周日为0,转换为7
|
||||
var daysInMonth = lastDay.getDate();
|
||||
|
||||
// 清空日历
|
||||
var calendarGrid = $('#calendarGrid');
|
||||
calendarGrid.empty();
|
||||
|
||||
// 添加星期标题
|
||||
var weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
||||
weekdays.forEach(function(day) {
|
||||
calendarGrid.append('<div class="calendar-weekday">' + day + '</div>');
|
||||
});
|
||||
|
||||
// 添加空白日期(第一天之前)
|
||||
for (var i = 1; i < firstDayOfWeek; i++) {
|
||||
calendarGrid.append('<div class="calendar-day empty"></div>');
|
||||
}
|
||||
|
||||
// 先渲染所有日期框架(显示日期号和加载提示)
|
||||
for (var day = 1; day <= daysInMonth; day++) {
|
||||
var currentDate = new Date(year, month, day);
|
||||
var dateStr = formatDate(currentDate);
|
||||
var isToday = dateStr === formatDate(new Date());
|
||||
|
||||
var dayHtml = '<div class="calendar-day' + (isToday ? ' today' : '') + '" data-date="' + dateStr + '" style="cursor:pointer;">';
|
||||
dayHtml += '<div class="calendar-day-header">' + day + '号</div>';
|
||||
dayHtml += '<div style="text-align:center;color:#999;padding:20px 0;">加载中...</div>';
|
||||
dayHtml += '</div>';
|
||||
calendarGrid.append(dayHtml);
|
||||
}
|
||||
|
||||
// 绑定日期点击事件
|
||||
$('#calendarGrid').off('click', '.calendar-day').on('click', '.calendar-day', function() {
|
||||
var clickedDate = $(this).attr('data-date');
|
||||
if (clickedDate && !$(this).hasClass('empty')) {
|
||||
showDateDetails(clickedDate);
|
||||
}
|
||||
});
|
||||
|
||||
// 异步加载数据并更新
|
||||
var startDate = new Date(year, month, 1);
|
||||
var endDate = new Date(year, month + 1, 0);
|
||||
loadMonthData(selectedRoomId, startDate, endDate);
|
||||
}
|
||||
|
||||
// 加载月度数据
|
||||
function loadMonthData(roomId, startDate, endDate) {
|
||||
var daysToLoad = [];
|
||||
|
||||
// 生成日期列表
|
||||
var currentDate = new Date(startDate);
|
||||
while (currentDate <= endDate) {
|
||||
daysToLoad.push(new Date(currentDate));
|
||||
currentDate.setDate(currentDate.getDate() + 1);
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
console.log('开始加载数据,房间ID:', roomId, '日期数量:', daysToLoad.length);
|
||||
}
|
||||
|
||||
// 批量加载数据,每个日期加载完成后立即更新
|
||||
daysToLoad.forEach(function(date) {
|
||||
var dateStr = formatDate(date);
|
||||
var url = layui.setter.apiUrl + "Api/SQRoomPricing/GetRoomListWithSlots?date=" + encodeURIComponent(dateStr) + "&showOnlyAvailable=false";
|
||||
|
||||
if (debug) {
|
||||
console.log('请求URL:', url);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(e) {
|
||||
if (debug) {
|
||||
console.log('日期:', dateStr, '返回数据:', e);
|
||||
}
|
||||
|
||||
var dayElement = $('#calendarGrid').find('.calendar-day[data-date="' + dateStr + '"]');
|
||||
|
||||
if (debug) {
|
||||
console.log('查找元素:', dateStr, '找到:', dayElement.length);
|
||||
}
|
||||
|
||||
if (dayElement.length === 0) {
|
||||
console.warn('未找到日期元素:', dateStr);
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空加载提示(保留日期标题)
|
||||
dayElement.find('div:not(.calendar-day-header)').remove();
|
||||
|
||||
if (e.code === 0 && e.data && Array.isArray(e.data)) {
|
||||
// 注意:roomId 可能是字符串,需要类型转换比较
|
||||
var roomData = e.data.find(function(r) {
|
||||
return r.id == roomId || r.id == parseInt(roomId);
|
||||
});
|
||||
|
||||
if (debug) {
|
||||
console.log('查找房间:', roomId, '找到:', roomData ? '是' : '否');
|
||||
}
|
||||
|
||||
if (roomData && roomData.time_slots && Array.isArray(roomData.time_slots)) {
|
||||
var dayContentHtml = '';
|
||||
|
||||
// 显示价格信息
|
||||
var firstSlot = roomData.time_slots[0];
|
||||
if (firstSlot && firstSlot.standard_price) {
|
||||
dayContentHtml += '<div class="calendar-day-price">' + firstSlot.standard_price + '元/时段</div>';
|
||||
}
|
||||
if (firstSlot && firstSlot.member_price > 0) {
|
||||
dayContentHtml += '<div class="calendar-day-member">会员价</div>';
|
||||
}
|
||||
|
||||
// 显示时段状态
|
||||
dayContentHtml += '<div class="calendar-day-slots">';
|
||||
roomData.time_slots.forEach(function(slot) {
|
||||
var statusClass = 'unavailable';
|
||||
var statusIcon = '✗';
|
||||
|
||||
if (slot.status === 'available') {
|
||||
statusClass = 'available';
|
||||
statusIcon = '◯';
|
||||
} else if (slot.status === 'reserved') {
|
||||
statusClass = 'reserved';
|
||||
statusIcon = '✓ ';
|
||||
}else if( slot.status === 'using'){
|
||||
statusClass = 'using';
|
||||
statusIcon = '✗';
|
||||
}
|
||||
|
||||
dayContentHtml += '<div class="calendar-slot ' + statusClass + '">' +
|
||||
slot.slot_name + statusIcon + '</div>';
|
||||
});
|
||||
dayContentHtml += '</div>';
|
||||
|
||||
dayElement.append(dayContentHtml);
|
||||
} else {
|
||||
dayElement.append('<div style="text-align:center;color:#999;padding:10px 0;">无房间数据</div>');
|
||||
}
|
||||
} else {
|
||||
var errorMsg = e.msg || '加载失败';
|
||||
dayElement.append('<div style="text-align:center;color:#f44336;padding:10px 0;">' + errorMsg + '</div>');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('请求失败:', dateStr, status, error);
|
||||
var dayElement = $('#calendarGrid').find('.calendar-day[data-date="' + dateStr + '"]');
|
||||
if (dayElement.length > 0) {
|
||||
dayElement.find('div:not(.calendar-day-header)').remove();
|
||||
dayElement.append('<div style="text-align:center;color:#f44336;padding:10px 0;">网络错误</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 格式化日期为 YYYY-MM-DD
|
||||
function formatDate(date) {
|
||||
var year = date.getFullYear();
|
||||
var month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
var day = date.getDate().toString().padStart(2, '0');
|
||||
return year + '-' + month + '-' + day;
|
||||
}
|
||||
|
||||
// 展示日期详情弹窗
|
||||
function showDateDetails(dateStr) {
|
||||
if (!selectedRoomId) {
|
||||
layer.msg('请先选择房间');
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找对应的房间名称
|
||||
var selectedRoom = roomOptions.find(function(r) {
|
||||
return r.id == selectedRoomId;
|
||||
});
|
||||
var roomName = selectedRoom ? selectedRoom.name : '未知房间';
|
||||
|
||||
// 显示加载提示
|
||||
var loadingIndex = layer.load(1, { shade: [0.1, '#fff'] });
|
||||
|
||||
// 请求详细数据
|
||||
var url = layui.setter.apiUrl + "Api/SQRoomPricing/GetDateDetails?date=" + encodeURIComponent(dateStr) + "&roomId=" + selectedRoomId;
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(e) {
|
||||
layer.close(loadingIndex);
|
||||
|
||||
if (e.code === 0 && e.data) {
|
||||
var data = e.data;
|
||||
|
||||
// 构建弹窗内容
|
||||
var content = '<div style="padding: 20px;">';
|
||||
|
||||
// 日期和房间信息
|
||||
content += '<div class="layui-row">';
|
||||
content += ' <div class="layui-col-md12">';
|
||||
content += ' <div class="layui-card">';
|
||||
content += ' <div class="layui-card-header"><h3>日期信息</h3></div>';
|
||||
content += ' <div class="layui-card-body">';
|
||||
content += ' <div class="layui-row">';
|
||||
content += ' <div class="layui-col-md6">';
|
||||
content += ' <p><strong>日期:</strong>' + dateStr + '</p>';
|
||||
content += ' <p><strong>房间:</strong>' + roomName + '</p>';
|
||||
content += ' </div>';
|
||||
content += ' <div class="layui-col-md6">';
|
||||
content += ' <p><strong>星期:</strong>' + getWeekDay(dateStr) + '</p>';
|
||||
content += ' </div>';
|
||||
content += ' </div>';
|
||||
content += ' </div>';
|
||||
content += ' </div>';
|
||||
content += ' </div>';
|
||||
content += '</div>';
|
||||
|
||||
// 时段详情
|
||||
if (data.time_slots && data.time_slots.length > 0) {
|
||||
content += '<div class="layui-row" style="margin-top: 20px;">';
|
||||
content += ' <div class="layui-col-md12">';
|
||||
content += ' <div class="layui-card">';
|
||||
content += ' <div class="layui-card-header"><h3>时段详情</h3></div>';
|
||||
content += ' <div class="layui-card-body">';
|
||||
content += ' <table id="dateDetailsTable" lay-filter="dateDetailsTable"></table>';
|
||||
content += ' </div>';
|
||||
content += ' </div>';
|
||||
content += ' </div>';
|
||||
content += '</div>';
|
||||
}
|
||||
|
||||
// 预约详情列表(按时段分组)
|
||||
content += '<div id="reservationsBySlot"></div>';
|
||||
|
||||
content += '</div>';
|
||||
|
||||
// 弹出窗口
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: roomName + ' - ' + dateStr + ' 详情',
|
||||
area: ['1200px', '90%'],
|
||||
content: content,
|
||||
success: function(layero, index) {
|
||||
// 渲染时段详情表格
|
||||
if (data.time_slots && data.time_slots.length > 0) {
|
||||
var tableData = data.time_slots.map(function(slot) {
|
||||
return {
|
||||
slot_name: slot.slot_name || '',
|
||||
status: slot.status || '',
|
||||
price_desc_standard: slot.price_desc_standard || '',
|
||||
price_desc_member: slot.price_desc_member || '',
|
||||
participant_avatars: slot.participant_avatars || [],
|
||||
reservation_count: slot.reservation_count || 0
|
||||
};
|
||||
});
|
||||
|
||||
table.render({
|
||||
elem: '#dateDetailsTable',
|
||||
data: tableData,
|
||||
page: false,
|
||||
cols: [[
|
||||
{ field: 'slot_name', title: '时段', width: 100 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
width: 100,
|
||||
templet: function(d) {
|
||||
var statusText = '';
|
||||
var statusColor = '';
|
||||
if (d.status === 'available') {
|
||||
statusText = '可用';
|
||||
statusColor = '#5FB878';
|
||||
} else if (d.status === 'reserved') {
|
||||
statusText = '已预约';
|
||||
statusColor = '#FFB800';
|
||||
} else if (d.status === 'using') {
|
||||
statusText = '使用中';
|
||||
statusColor = '#999';
|
||||
} else if (d.status === 'unavailable') {
|
||||
statusText = '不可用';
|
||||
statusColor = '#FF5722';
|
||||
} else {
|
||||
statusText = d.status;
|
||||
statusColor = '#999';
|
||||
}
|
||||
return '<span style="background: ' + statusColor + '; color: white; padding: 2px 8px; border-radius: 3px; font-size: 12px;">' + statusText + '</span>';
|
||||
}
|
||||
},
|
||||
{ field: 'price_desc_standard', title: '普通价格描述', width: 150 },
|
||||
{ field: 'price_desc_member', title: '会员价格描述', width: 150 },
|
||||
{
|
||||
field: 'participant_avatars',
|
||||
title: '预约人头像',
|
||||
width: 200,
|
||||
templet: function(d) {
|
||||
if (!d.participant_avatars || d.participant_avatars.length === 0) {
|
||||
return '<span style="color: #999;">无</span>';
|
||||
}
|
||||
var html = '<div style="display: flex; gap: 4px; flex-wrap: wrap;">';
|
||||
d.participant_avatars.forEach(function(avatar) {
|
||||
html += '<img src="' + avatar + '" style="width:28px;height:28px;border-radius:50%;object-fit:cover;" onerror="this.src=\'/images/default-avatar.png\'" />';
|
||||
});
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{ field: 'reservation_count', title: '预约人数', width: 100 }
|
||||
]]
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染按时段分组的预约详情
|
||||
if (data.reservations_by_slot) {
|
||||
var slotNames = ['凌晨', '上午', '下午', '晚上'];
|
||||
var reservationsBySlotContainer = $('#reservationsBySlot');
|
||||
|
||||
for (var slotType = 0; slotType <= 3; slotType++) {
|
||||
var slotReservations = data.reservations_by_slot[slotType];
|
||||
|
||||
if (slotReservations && slotReservations.length > 0) {
|
||||
// 时段标题
|
||||
var slotHtml = '<div class="layui-row" style="margin-top: 20px;">';
|
||||
slotHtml += ' <div class="layui-col-md12">';
|
||||
slotHtml += ' <h3 style="padding: 10px; background: #f8f8f8; border-left: 4px solid #009688;">预约详情列表(' + slotNames[slotType] + ')</h3>';
|
||||
|
||||
// 遍历该时段的每个预约
|
||||
slotReservations.forEach(function(reservation, idx) {
|
||||
slotHtml += ' <div class="layui-card" style="margin-top: 10px;">';
|
||||
slotHtml += ' <div class="layui-card-header">';
|
||||
slotHtml += ' <div class="layui-row">';
|
||||
slotHtml += ' <div class="layui-col-md3"><strong>组局名称:</strong>' + (reservation.title || '无') + '</div>';
|
||||
slotHtml += ' <div class="layui-col-md2"><strong>参与人数:</strong>' + (reservation.player_count || 0) + '人</div>';
|
||||
slotHtml += ' <div class="layui-col-md2"><strong>鸽子费:</strong>' + (reservation.deposit_fee || 0) + '元</div>';
|
||||
slotHtml += ' <div class="layui-col-md3"><strong>最晚到店:</strong>' + (reservation.latest_arrival_time || '无') + '</div>';
|
||||
slotHtml += ' </div>';
|
||||
slotHtml += ' <div class="layui-row" style="margin-top: 5px;">';
|
||||
slotHtml += ' <div class="layui-col-md3"><strong>玩法类型:</strong>' + (reservation.game_type || '无') + '</div>';
|
||||
slotHtml += ' <div class="layui-col-md3"><strong>具体规则:</strong>' + (reservation.game_rule || '无') + '</div>';
|
||||
slotHtml += ' <div class="layui-col-md4"><strong>开始时间:</strong>' + (reservation.start_time || '无') + '</div>';
|
||||
slotHtml += ' </div>';
|
||||
slotHtml += ' </div>';
|
||||
slotHtml += ' <div class="layui-card-body">';
|
||||
slotHtml += ' <table id="participantsTable_' + slotType + '_' + reservation.id + '" lay-filter="participantsTable_' + slotType + '_' + reservation.id + '"></table>';
|
||||
slotHtml += ' </div>';
|
||||
slotHtml += ' </div>';
|
||||
});
|
||||
|
||||
slotHtml += ' </div>';
|
||||
slotHtml += '</div>';
|
||||
|
||||
reservationsBySlotContainer.append(slotHtml);
|
||||
|
||||
// 渲染每个预约的参与者表格
|
||||
slotReservations.forEach(function(reservation, idx) {
|
||||
var participants = reservation.participants || [];
|
||||
|
||||
table.render({
|
||||
elem: '#participantsTable_' + slotType + '_' + reservation.id,
|
||||
data: participants,
|
||||
page: false,
|
||||
cols: [[
|
||||
{ field: 'id', title: 'ID', width: 60 },
|
||||
{ field: 'user_id', title: '用户ID', width: 80 },
|
||||
{ field: 'user_name', title: '用户昵称', width: 120 },
|
||||
{
|
||||
field: 'avatar_image',
|
||||
title: '用户头像',
|
||||
width: 80,
|
||||
templet: function(d) {
|
||||
if (d.avatar_image) {
|
||||
return '<img src="' + d.avatar_image + '" style="width:40px;height:40px;border-radius:50%;object-fit:cover;" onerror="this.src=\'/images/default-avatar.png\'" />';
|
||||
}
|
||||
return '<span style="color: #999;">无</span>';
|
||||
}
|
||||
},
|
||||
{ field: 'join_time', title: '参与时间', width: 150 },
|
||||
{
|
||||
field: 'role',
|
||||
title: '角色',
|
||||
width: 80,
|
||||
templet: function(d) {
|
||||
return d.role === 1 ? '<span style="color: #ff5722;">发起者</span>' : '参与者';
|
||||
}
|
||||
},
|
||||
{ field: 'quit_time', title: '退出时间', width: 150 },
|
||||
{
|
||||
field: 'is_refund',
|
||||
title: '鸽子费状态',
|
||||
width: 100,
|
||||
templet: function(d) {
|
||||
var refundText = '';
|
||||
var refundColor = '';
|
||||
switch(d.is_refund) {
|
||||
case 0:
|
||||
refundText = '无需退款';
|
||||
refundColor = '#999';
|
||||
break;
|
||||
case 1:
|
||||
refundText = '未付鸽子费';
|
||||
refundColor = '#1E9FFF';
|
||||
break;
|
||||
case 2:
|
||||
refundText = '已付鸽子费';
|
||||
refundColor = '#FFB800';
|
||||
break;
|
||||
case 3:
|
||||
refundText = '退款中';
|
||||
refundColor = '#5FB878';
|
||||
break;
|
||||
case 4:
|
||||
refundText = '退款成功';
|
||||
refundColor = '#5FB878';
|
||||
break;
|
||||
case 9:
|
||||
refundText = '退款失败';
|
||||
refundColor = '#FF5722';
|
||||
break;
|
||||
default:
|
||||
refundText = '未知状态';
|
||||
refundColor = '#999';
|
||||
}
|
||||
return '<span style="background: ' + refundColor + '; color: white; padding: 2px 6px; border-radius: 3px; font-size: 12px;">' + refundText + '</span>';
|
||||
}
|
||||
},
|
||||
{ field: 'remarks', title: '备注', minWidth: 150 }
|
||||
]]
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
layer.msg(e.msg || '获取数据失败');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
layer.close(loadingIndex);
|
||||
console.error('请求失败:', status, error);
|
||||
layer.msg('获取数据失败,请稍后重试');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取星期几
|
||||
function getWeekDay(dateStr) {
|
||||
var date = new Date(dateStr);
|
||||
var weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
return weekDays[date.getDay()];
|
||||
}
|
||||
|
||||
//重载form
|
||||
form.render();
|
||||
});
|
||||
};
|
||||
|
||||
// 查看预约详情(全局函数,供表格调用)
|
||||
function viewReservationDetail(reservationId) {
|
||||
// 跳转到预约管理页面或打开预约详情弹窗
|
||||
layer.msg('预约ID: ' + reservationId + ',可跳转到预约详情页面');
|
||||
// 实际使用时可以调用预约详情接口
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--设置是否启用-->
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user