<template>
	<view class="container">
		<view class="header">
			<text class="title">接种留观人员信息表</text>
			<text class="current-time">{{ currentTime }}</text>
		</view>

		<!-- 表格 -->
		<view class="table-container">
			<!-- 表头 -->
			<view class="table-header">
				<view class="table-row header-row">
					<view class="cell name title_color">姓名</view>
					<view class="cell time title_color">留观时间</view>
					<view class="cell time title_color">离开时间</view>
					<view class="cell status title_color">状态</view>
				</view>
			</view>

			<!-- 表格主体 - 支持纵向轮播滚动 -->
			<view class="table-body-container">
				<view class="table-body-wrapper" :class="{ 'animate': shouldAnimate }">
					<view v-for="(item, index) in carouselList" :key="index" class="table-row body-row"
						:class="`item-${getClass(item.status)}`">
						<view class="cell name">{{ item.patientName }}</view>
						<view class="cell time">{{ getTime(item.createTime) }}</view>
						<view class="cell time">{{ getTime(item.outTime) }}</view>
						<view class="cell status">
							<text class="status-tag" :class="`status-${getClass(item.status)}`">
								{{ getStatusText(item.status) }}
							</text>
						</view>
					</view>
				</view>
			</view>
		</view>

		<!-- 连接状态与IP编辑栏 -->
		<view class="status-bar-bottom" :class="`status-${connectionStatus}`">
			<view class="status-text-box">
				<text class="dot" :class="`dot-${connectionStatus}`">●</text>
				<text class="status-text">{{ connectionText }}</text>
			</view>
			<!-- IP 编辑区域 -->
			<view class="ip-input-group">
				<text class="ip-label">IP:</text>
				<input type="text" :value="serverIp" @input="onIpInput" placeholder="192.168.0.41" class="ip-input" />
				<text class="colon">:</text>
				<input type="number" :value="serverPort" @input="onPortInput" placeholder="8811" class="port-input" />
				<button class="btn-reconnect" size="mini" @click="handleReconnect">
					更新并重连
				</button>
				<button class="btn-reconnect" size="mini" @click="goback">
					测试
				</button>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				currentTime: '', // 当前时间
				timeTimer: null, // 时间更新定时器
				// WebSocket 实例
				ws: null,
				reconnectTimer: null,
				heartbeatTimer: null, // 心跳定时器
				heartbeatTimeout: null, // 心跳响应超时计时器
				heartbeatInterval: 30000, // 30秒发一次心跳
				heartbeatTimeoutTime: 10000, // 10秒内未响应视为超时
				isPongReceived: true, // 标记是否收到 pong
				// 连接状态
				connectionStatus: 'connecting', // connecting, connected, disconnected
				// 留观数据列表
				list: [],
				statusText: {
					observing: '留观中',
					completed: '留观完成，可离开',
					warning: '提前离开',
					hasleft: '已离开',
				},
				// 可编辑的服务器地址
				serverIp: '192.168.11.132',
				serverPort: '8811',
			};
		},
		computed: {
			connectionText() {
				const {
					connectionStatus,
					serverIp,
					serverPort
				} = this;
				return {
					connecting: `正在连接 ${serverIp}:${serverPort}...`,
					connected: `已连接到 ${serverIp}:${serverPort}`,
					disconnected: `连接已断开`
				} [connectionStatus];
			},
			// 轮播列表数据
			carouselList() {
				if (this.list.length === 0) return [];
				// 如果数据较少，不需要轮播
				if (this.list.length <= 5) return this.list;
				// 复制数据实现无缝轮播
				return [...this.list, ...this.list];
			},
			// 是否需要启动轮播动画
			shouldAnimate() {
				return this.list.length > 5;
			}
		},
		methods: {
			getClass(status) {
				var title = '';
				if (status == 0) {
					title = 'observing'
				} else if (status == 1) {
					title = 'completed'
				} else if (status == 3) {
					title = 'warning'
				} else if (status == 4) {
					title = 'completed'
				}
				return title
			},
			getStatusText(status) {
				var title = '';
				if (status == 0) {
					title = this.statusText.observing
				} else if (status == 1) {
					title = this.statusText.completed
				} else if (status == 3) {
					title = this.statusText.warning
				} else if (status == 4) {
					title = this.statusText.hasleft
				}
				return title
			},
			// 格式时分
			getTime(dateTimeStr) {
				// 创建 Date 对象
				const date = new Date(dateTimeStr);

				// 提取小时和分钟，并格式化为两位数
				const hours = String(date.getHours()).padStart(2, '0');
				const minutes = String(date.getMinutes()).padStart(2, '0');

				const time = `${hours}:${minutes}`;
				return time
			},
			// 格式化时间范围：09:00 - 09:30
			formatTimeRange(start, end) {
				return start && end ? `${start} - ${end}` : '--';
			},
			// 输入框事件
			onIpInput(e) {
				this.serverIp = e.detail.value;
			},
			onPortInput(e) {
				this.serverPort = e.detail.value;
			},
			// 构建 WebSocket URL
			getWebSocketUrl() {
				return `ws://${this.serverIp}:${this.serverPort}`;
			},
			// 处理重连
			handleReconnect() {
				uni.showLoading({
					title: '正在重连...'
				});
				this.connectionStatus = 'connecting';
				// 关闭旧连接
				if (this.ws) {
					this.ws.close({
						success: () => {
							console.log('旧连接已关闭');
							this.connectWebSocket('link');
						},
						fail: () => {
							this.connectWebSocket('link'); // 即使关闭失败也尝试重连
						}
					});
				} else {
					this.connectWebSocket('link');
				}
				setTimeout(() => {
					uni.hideLoading();
				}, 2000);
			},
			// 建立 WebSocket 连接
			connectWebSocket(type) {
				const url = this.getWebSocketUrl();
				// 创建 SocketTask
				this.ws = uni.connectSocket({
					url: url,
					success: (res) => {
						console.log('connectSocket success', res);
					},
					fail: (err) => {
						console.error('connectSocket 失败', err);
						this.connectionStatus = 'disconnected';
						this.reconnect();
					}
				});
				// --- WebSocket 事件监听 ---
				this.ws.onOpen((res) => {
					// console.log('WebSocket 连接成功', res);
					this.connectionStatus = 'connected';
					this.ws.send({
						data: type,
					});
					// ✅ 连接成功后启动心跳
					this.startHeartbeat();
					uni.hideLoading();
				});
				this.ws.onMessage((res) => {
					// ✅ 处理心跳响应 pong
					if (res.data === 'PONG' || res.data === '{"type":"PONG"}') {
						this.isPongReceived = true;
						// console.log('收到 pong，心跳正常');
						// ✅ 清除等待 pong 的超时计时器
						if (this.heartbeatTimeout) {
							clearTimeout(this.heartbeatTimeout);
							this.heartbeatTimeout = null;
						}
						return;
					}
					try {
						const data = JSON.parse(res.data);
						// console.log('收到消息:', data);
						this.handleMessage(data);
					} catch (e) {
						console.warn('非 JSON 消息，已忽略', res.data);
					}
				});
				this.ws.onClose((res) => {
					console.log('WebSocket 连接关闭', res);
					this.connectionStatus = 'disconnected';
					this.stopHeartbeat(); // 停止心跳
					this.reconnect(); // 断线重连
				});
				this.ws.onError((err) => {
					console.error('WebSocket 错误', err);
					this.connectionStatus = 'disconnected';
				});
			},
			// 处理收到的消息
			handleMessage(data) {
				// 1. 全量数据：数组（不是对象，或没有 action 字段）
				if (data.action == 'link') {
					this.updateListWithArray(data.data);
					return;
				}
				// 2. 增量操作：对象
				if (typeof data === 'object' && data !== null) {
					const action = data.action;

					if (!action) {
						console.warn('消息缺少 action 字段', data);
						return;
					}
					switch (action) {
						case 'add':
						case 'create':
							this.batchAdd(data.data);
							break;
						case 'update':
							this.batchUpdate(data.data);
							break;
						case 'remove':
						case 'delete':
							this.batchRemove(data.data);
							break;
						default:
							console.warn('未知操作类型:', action);
					}
				} else {
					console.warn('收到未知格式消息:', data);
				}
			},
			// 批量新增
			batchAdd(data) {
				if (!data) return;
				const items = Array.isArray(data) ? data : [data]; // 兼容单条
				const validItems = items.filter(item => item && item.id !== undefined);
				if (validItems.length === 0) {
					console.warn('没有有效数据用于新增', data);
					return;
				}
				// 避免重复添加
				const updated = [...this.list];
				validItems.forEach(item => {
					const index = updated.findIndex(i => i.id == item.id);
					if (index > -1) {
						// 已存在 → 更新
						updated[index] = {
							...updated[index],
							...item
						};
					} else {
						updated.unshift(item);
					}
				});
				this.list = updated;
			},
			// 批量修改
			batchUpdate(data) {
				if (!data) return;
				const items = Array.isArray(data) ? data : [data]; // 支持单条或数组
				const updated = [...this.list];
				items.forEach(item => {
					if (!item || item.id === undefined) return;
					const index = updated.findIndex(i => i.id == item.id);
					if (index > -1) {
						updated[index] = {
							...updated[index],
							...item
						};
					}
					// else { this.list.push(item); } // 可选：当作新增
				});
				this.list = updated;
			},
			// 批量删除
			batchRemove(data) {
				let idsToRemove = [];
				if (Array.isArray(data.id)) {
					// remove: { id: [1,2,3] }
					idsToRemove = data.id;
				} else if (data.id !== undefined) {
					// remove: { id: 1 }
					idsToRemove = [data.id];
				} else if (Array.isArray(data.data)) {
					// remove: { data: [ {id:1}, {id:2} ] }
					idsToRemove = data.data.map(item => item.id).filter(id => id !== undefined);
				} else {
					idsToRemove = [data.id];
					console.warn('无法解析删除指令', data);
					return;
				}
				if (idsToRemove.length === 0) return;
				const updated = this.list.filter(item => !idsToRemove.includes(item.id));
				this.list = updated;
			},
			updateListWithArray(newList) {
				if (!Array.isArray(newList)) return;

				const map = new Map();
				newList.forEach(item => {
					if (item.id !== undefined) {
						map.set(item.id, item);
					}
				});

				const updated = [...this.list];

				// 更新或新增
				for (const [id, item] of map.entries()) {
					const index = updated.findIndex(i => i.id == id);
					if (index > -1) {
						updated[index] = item;
					} else {
						updated.push(item);
					}
				}
				const final = updated.filter(item => map.has(item.id));
				this.list = final;
			},
			// 断线重连（指数退避）
			reconnect() {
				if (this.reconnectTimer) return; // 防止重复定时器
				this.reconnectTimer = setTimeout(() => {
					console.log('正在尝试重新连接...');
					this.connectWebSocket('link');
					this.reconnectTimer = null;
				}, 3000);
			},
			// 启动心跳
			startHeartbeat() {
				console.log(2324)
				// 清除旧心跳
				this.stopHeartbeat();
				// 发送 ping 的定时器
				this.heartbeatTimer = setInterval(() => {
					if (this.ws && this.connectionStatus === 'connected') {
						// 检查上一次的 pong 是否已收到，未收到则判定为断线
						if (!this.isPongReceived) {
							console.warn('上一次心跳未收到 pong，可能已断线');
							this.ws.close();
							return;
						}
						// 发送心跳
						this.isPongReceived = false; // 等待 pong
						// 设置超时检测：10秒内没收到 pong 就断开
						this.heartbeatTimeout = setTimeout(() => {
							if (!this.isPongReceived) {
								console.warn('⚠️ 心跳超时：未在规定时间内收到 pong，即将重连');
								uni.showToast({
									title: '连接异常，正在重连...',
									icon: 'none',
									duration: 2000
								});
								this.ws.close(); // 触发 onClose → 重连
							}
						}, this.heartbeatTimeoutTime); // 使用配置的超时时间（如 10000ms）
						try {
							this.ws.send({
								data: 'PING',
								success: () => {
									// console.log('ping 已发送');
								},
								fail: (err) => {
									console.error('ping 发送失败', err);
									this.ws.close();
								}
							});
						} catch (e) {
							console.error('发送 ping 异常', e);
							this.ws.close();
						}
					}
				}, this.heartbeatInterval);
				// 可选：启动响应超时检测（更严格）
				// 通常靠"发 ping 后未收到 pong"即可判断
			},
			// 停止心跳（断开连接时调用）
			stopHeartbeat() {
				if (this.heartbeatTimer) {
					clearInterval(this.heartbeatTimer);
					this.heartbeatTimer = null;
				}
				if (this.heartbeatTimeout) {
					clearTimeout(this.heartbeatTimeout);
					this.heartbeatTimeout = null;
				}
			},
			updateCurrentTime() {
				const now = new Date();
				const year = now.getFullYear();
				const month = String(now.getMonth() + 1).padStart(2, '0');
				const day = String(now.getDate()).padStart(2, '0');
				const hours = String(now.getHours()).padStart(2, '0');
				const minutes = String(now.getMinutes()).padStart(2, '0');
				const seconds = String(now.getSeconds()).padStart(2, '0');
				this.currentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
			},
			goback() {
				uni.navigateTo({
					url: '/pages/index/home'
				});
			}
		},
		mounted() {
			this.connectWebSocket('link');
			this.updateCurrentTime(); // 立即更新一次时间
			this.timeTimer = setInterval(() => {
				this.updateCurrentTime();
			}, 1000); // 每秒更新一次时间
		},
		// 页面卸载时关闭连接
		beforeDestroy() {
			if (this.ws) {
				this.ws.close();
			}
			if (this.reconnectTimer) {
				clearTimeout(this.reconnectTimer);
			}
			this.stopHeartbeat();
			// 清理时间更新定时器
			if (this.timeTimer) {
				clearInterval(this.timeTimer);
			}
		}
	};
</script>

<style lang="scss" scoped>
	.container {
		background-color: #f4f6f8;
		display: flex;
		flex-direction: column;
		height: 100vh;
		padding: 20rpx 30rpx 30rpx 30rpx;
		background-color: #f4f6f8;
		font-family: 'Arial', 'Microsoft YaHei', sans-serif;
		box-sizing: border-box;
		overflow: hidden;
		/* 隐藏页面滚动，让轮播在表格内进行 */
	}

	.header {
		padding-top: 10rpx;
		text-align: center;
		margin-bottom: 30rpx;
		position: relative;
		display: flex;
		align-items: center;
		justify-content: center;
		height: 100rpx;
	}

	.title {
		font-size: 60rpx;
		font-weight: bold;
		color: #1e3a8a;
	}

	.current-time {
		display: flex;
		align-items: center;
		position: absolute;
		display: flex;
		height: 100rpx;
		right: 0rpx;
		font-size: 55rpx;
		color: #666;
		background-color: rgba(30, 58, 138, 0.1);
		padding: 0rpx 30rpx;
		border-radius: 12rpx;
		font-weight: bold;
	}

	.table-container {
		flex: 1;
		margin-bottom: 30rpx;
		border-radius: 24rpx;
		overflow: hidden;
		box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
		background-color: #ffffff;
		border: 2rpx solid #e0e0e0;
		display: flex;
		flex-direction: column;
	}

	.table-header {
		background-color: #f0f4f8;
		color: white;
		font-weight: bold;
	}

	.header-row {
		background-color: #1e3a8a;
		color: white;
		font-weight: bold;
	}

	.table-row {
		display: flex;
		border-bottom: 2rpx solid #dedede;
	}

	.body-row {
		padding: 10rpx 0rpx;
	}

	.table-row:last-child {
		border-bottom: none;
	}

	.cell {
		padding: 24rpx 16rpx;
		font-size: 50rpx;
		text-align: center;
		color: #333;
	}

	.name {
		width: 25%;
	}

	.time {
		width: 25%;
	}

	.status {
		width: 25%;
	}

	.status-tag {
		padding: 8rpx 20rpx;
		border-radius: 32rpx;
		font-size: 48rpx;
		font-weight: 500;
	}

	.status-observing {
		background-color: #e3f2fd;
		color: #007bff;
		border: 2rpx solid #9acffa;
	}

	.status-completed {
		background-color: #e8f5e8;
		color: #28a745;
		border: 2rpx solid #8bc34a;
	}

	.status-warning {
		background-color: #ffebee; // 浅红背景
		color: #c62828; // 深红色文字
		border: 2rpx solid #ef9a9a;
	}

	.item-observing {
		background-color: #e3f2fd;
	}

	.item-completed {
		background-color: #e8f5e8;
	}

	.item-warning {
		background-color: #ffebee; // 浅红背景
	}

	.item-hasleft {
		background-color: #e3f2fd;
	}

	.table-header {
		flex: 0 0 auto;
		overflow: hidden;
	}

	/* 保持表格形式的轮播样式 */
	.table-body-container {
		flex: 1;
		overflow: hidden;
		background-color: #fafafa;
		position: relative;
	}

	.table-body-wrapper {
		display: block;
	}

	.table-body-wrapper.animate {
		animation: scrollUp 20s linear infinite;
	}

	@keyframes scrollUp {
		0% {
			transform: translateY(0);
		}

		100% {
			transform: translateY(calc(-50% - 1px));
			/* 减去1px确保无缝衔接 */
		}
	}

	.empty {
		text-align: center;
		padding: 60rpx;
		color: #999;
		font-style: italic;
		font-size: 40rpx;
	}

	// 连接状态栏
	.status-bar-bottom {
		padding: 24rpx 30rpx;
		background-color: #ffffff;
		box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
		border-radius: 24rpx;
		display: flex;
		gap: 16rpx;
	}

	.status-text-box {
		display: flex;
		align-items: center;
		font-size: 28rpx;
	}

	.dot {
		color: #d32f2f;
		font-size: 40rpx;
	}

	.dot-connecting {
		color: #f9ae3d;
	}

	.dot-connected {
		color: #28a745;
	}

	.dot-disconnected {
		color: #d32f2f;
	}

	.status-text {
		color: #555;
	}

	.status-text {
		margin-left: 16rpx;
	}

	.title_color {
		color: #fff;
	}

	// IP 输入区域
	.ip-input-group {
		display: flex;
		align-items: center;
		margin-left: 16rpx;
		flex-wrap: wrap;
		gap: 12rpx;
	}

	.ip-label {
		font-size: 28rpx;
		color: #555;
	}

	.ip-input,
	.port-input {
		border: 2rpx solid #ddd;
		border-radius: 12rpx;
		padding: 12rpx 20rpx;
		font-size: 28rpx;
		width: 200rpx;
	}

	.port-input {
		width: 160rpx;
	}

	.colon {
		font-size: 32rpx;
		color: #666;
		margin: 0 8rpx;
	}

	.btn-reconnect {
		background-color: #007aff;
		color: white;
		font-size: 24rpx;
		padding: 0 16rpx;
		border-radius: 12rpx;
	}
</style>