<template>
	<div class="chat-box" @click="closeRefBox()" @mousemove="readedMessage()">
		<el-container>
			<el-header height="50px">
				<span>{{ title }}</span>
				<span title="群聊信息" v-show="isGroup" class="btn-side el-icon-more" @click="showSide = !showSide"></span>
				<span title="用户信息" v-show="isPrivate" class="btn-side el-icon-more" @click="showUserSide = !showUserSide"></span>
			</el-header>
			<el-main style="padding: 0;">
				<el-container>
					<el-container class="content-box">
						<el-main class="im-chat-main" id="chatScrollBox" @scroll="onScroll">
							<div class="im-chat-box">
								<div v-for="(msgInfo, idx) in showMessages" :key="showMinIdx + idx">
									<chat-message-item @call="onCall(msgInfo.type)" :mine="msgInfo.sendId == mine.id"
										:headImage="headImage(msgInfo)" :showName="showName(msgInfo)" :msgInfo="msgInfo"
										:groupMembers="groupMembers" @resend="onResendMessage" @delete="deleteMessage"
										@recall="recallMessage">
									</chat-message-item>
								</div>
							</div>
						</el-main>
						<div v-if="!isInBottom" class="scroll-to-bottom" @click="scrollToBottom">
							{{ newMessageSize > 0 ? newMessageSize + '条新消息' : '回到底部' }}
						</div>
						<el-footer height="220px" class="im-chat-footer">
							<div class="chat-tool-bar">
								<div title="表情" class="icon iconfont icon-emoji" ref="emotion"
									@click.stop="showEmotionBox()">
								</div>
								<div title="发送图片">
									<file-upload :action="'/image/upload'" :maxSize="5 * 1024 * 1024"
										:fileTypes="['image/jpeg', 'image/png', 'image/jpg', 'image/webp', 'image/gif']"
										@before="onImageBefore" @success="onImageSuccess" @fail="onImageFail">
										<i class="el-icon-picture-outline"></i>
									</file-upload>
								</div>
								<div title="快捷回复" class="el-icon-s-comment" @click.stop="openQuickReplyBox()"></div>
								<div title="回执消息" v-show="isGroup && memberSize <= 500"
									class="icon iconfont icon-receipt" :class="isReceipt ? 'chat-tool-active' : ''"
									@click="onSwitchReceipt">
								</div>
								<div title="聊天记录" class="el-icon-chat-dot-round" @click="showHistoryBox()"></div>
								<div title="转接客服" class="el-icon-service" @click="showChangeCustomerBox()" v-if="isPrivate"></div>
							</div>
							<div class="send-content-area">
								<ChatInput :ownerId="group.ownerId" ref="chatInputEditor" :group-members="groupMembers"
									@submit="sendMessage" />
								<div class="send-btn-area">
									<el-button type="primary" icon="el-icon-s-promotion"
										@click="notifySend()">发送</el-button>
								</div>
							</div>
						</el-footer>
					</el-container>
					<el-aside class="side-box" width="320px" v-if="showSide">
						<chat-group-side :group="group" :groupMembers="groupMembers" @reload="loadGroup(group.id)">
						</chat-group-side>
					</el-aside>
					<el-aside class="side-box user-info-side" width="320px" v-if="showUserSide">
						<div class="user-info-container">
							<div class="user-info-header">
								<img :src="userInfo.headImage || userInfo.headImageThumb" class="user-avatar-large" />
								<h3 class="user-nickname">{{ userInfo.nickName }}</h3>
								<p class="user-username">@{{ userInfo.userName }}</p>
							</div>
							<div class="user-info-content">
								<div class="info-item">
									<div class="info-label">用户IP</div>
									<div class="info-value">
										{{ formatIpDisplay(userInfo.lastLoginIp, ipLocation) }}
									</div>
								</div>
								<div class="info-item">
									<div class="info-label">来源地址</div>
									<div class="info-value">{{ userInfo.sourceUrl }}</div>
								</div>
								<div class="info-item">
									<div class="info-label">性别</div>
									<div class="info-value">{{ userInfo.sex === 1 ? '女' : '男' }}</div>
								</div>
								<div class="info-item">
									<div class="info-label">标签</div>
									<div class="info-value">
										<el-select
											v-model="selectedLabels"
											size="small"
											multiple
											filterable
											placeholder="请选择标签"
											:loading="labelOptionsLoading"
											@change="handleSelectLabels"
											style="width:100%;"
										>
											<el-option
												v-for="item in availableLabelOptions"
												:key="item.value"
												:label="item.label"
												:value="String(item.value)"
												:disabled="selectedLabels.includes(String(item.value))"
											/>
										</el-select>
									</div>
								</div>
								<div class="info-item">
									<div class="info-label">分组</div>
									<div class="info-value">
										<el-select
											v-model="selectedGroup"
											size="small"
											style="width:120px;"
											@change="handleSelectUserGroup"
										>
										<el-option
											v-for="item in groupOptions"
											:key="item.value"
											:label="item.label"
											:value="item.value"
										/>
										</el-select>
									</div>
								</div>
							</div>
						</div>
					</el-aside>
				</el-container>
			</el-main>

			<emotion ref="emoBox" @emotion="onEmotion"></Emotion>
			<chat-record :visible="showRecord" @close="closeRecordBox" @send="onSendRecord"></chat-record>
			<group-member-selector ref="rtcSel" :group="group" @complete="onInviteOk"></group-member-selector>
			<rtc-group-join ref="rtcJoin" :groupId="group.id"></rtc-group-join>
			<chat-history :visible="showHistory" :chat="chat" :friend="friend" :group="group"
				:groupMembers="groupMembers" @close="closeHistoryBox"></chat-history>
			<change-customer :visible="showChangeCustomer" :target-user-id="targetUserId" 
				@close="closeChangeCustomerBox" @transfer-success="onTransferSuccess"></change-customer>

			<!-- 快捷回复弹窗 -->
			<el-dialog
				title="快捷回复"
				:visible.sync="showQuickReplyBox"
				width="500px"
				append-to-body
				@close="closeQuickReplyBox"
			>
				<el-scrollbar height="300px">
					<div class="quick-reply-list">
						<div
							class="quick-item"
							v-for="item in quickReplyList"
							:key="item.id"
							@click="selectQuickReply(item)"
						>
							{{ item.replyContent }}
						</div>
						<div class="quick-empty" v-if="quickReplyList.length === 0 && !quickLoading">
							暂无快捷回复
						</div>
					</div>
				</el-scrollbar>
				<div slot="footer" class="dialog-footer">
					<el-button @click="showQuickReplyBox = false">关闭</el-button>
				</div>
			</el-dialog>
		</el-container>
	</div>
</template>

<script>
import ChatGroupSide from "./ChatGroupSide.vue";
import ChatMessageItem from "./ChatMessageItem.vue";
import FileUpload from "../common/FileUpload.vue";
import Emotion from "../common/Emotion.vue";
import ChatRecord from "./ChatRecord.vue";
import ChatHistory from "./ChatHistory.vue";
import ChatAtBox from "./ChatAtBox.vue"
import GroupMemberSelector from "../group/GroupMemberSelector.vue"
import RtcGroupJoin from "../rtc/RtcGroupJoin.vue"
import ChatInput from "./ChatInput";
import ChangeCustomer from "./ChangeCustomer.vue";

export default {
	name: "chatPrivate",
	components: {
		ChatInput,
		ChatMessageItem,
		FileUpload,
		ChatGroupSide,
		Emotion,
		ChatRecord,
		ChatHistory,
		ChatAtBox,
		GroupMemberSelector,
		RtcGroupJoin,
		ChangeCustomer
	},
	props: {
		chat: {
			type: Object
		}
	},
	data() {
		return {
			userInfo: {},
			group: {},
			groupMembers: [],
			groupOptions: [],
			groupOptionsLoading: false,
			selectedGroup: '', 
			labelOptions: [],
			labelOptionsLoading: false,
			selectedLabels: [],
			sendImageUrl: "",
			sendImageFile: "",
			placeholder: "",
			isReceipt: true,
			showRecord: false,
			showSide: false,
			showUserSide: true,
			showHistory: false,
			showChangeCustomer: false,
			showQuickReplyBox: false, // 快捷回复弹窗
			quickReplyList: [],      // 快捷回复列表
			quickLoading: false,     // 加载状态
			lockMessage: false,
			showMinIdx: 0,
			reqQueue: [],
			isSending: false,
			isInBottom: false,
			newMessageSize: 0,
			ipLocation: '',
			maxTmpId: 0
		}
	},
	methods: {
		moveChatToTop() {
			let chatIdx = this.chatStore.findChatIdx(this.chat);
			this.chatStore.moveTop(chatIdx);
		},
		closeRefBox() {
			this.$refs.emoBox.close();
		},
		onCall(type) {
			if (type == this.$enums.MESSAGE_TYPE.ACT_RT_VOICE) {
				this.showPrivateVideo('voice');
			} else if (type == this.$enums.MESSAGE_TYPE.ACT_RT_VIDEO) {
				this.showPrivateVideo('video');
			}
		},
		onSwitchReceipt() {
			this.isReceipt = !this.isReceipt;
			this.refreshPlaceHolder();
		},
		onImageSuccess(data, file) {
			let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
			msgInfo.content = JSON.stringify(data);
			msgInfo.receipt = this.isReceipt;
			this.sendMessageRequest(msgInfo).then((m) => {
				msgInfo.id = m.id;
				msgInfo.status = m.status;
				this.isReceipt = false;
				this.chatStore.updateMessage(msgInfo, file.chat);
			}).catch(() => {
				msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
				this.chatStore.updateMessage(msgInfo, file.chat);
			})
		},
		onImageFail(e, file) {
			let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
			msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
			this.chatStore.updateMessage(msgInfo, file.chat);
		},
		onImageBefore(file) {
			if (this.isBanned) {
				this.showBannedTip();
				return;
			}
			let url = URL.createObjectURL(file);
			let data = {
				originUrl: url,
				thumbUrl: url
			}
			let msgInfo = {
				tmpId: this.generateId(),
				fileId: file.uid,
				sendId: this.mine.id,
				content: JSON.stringify(data),
				sendTime: new Date().getTime(),
				selfSend: true,
				type: this.$enums.MESSAGE_TYPE.IMAGE,
				readedCount: 0,
				status: this.$enums.MESSAGE_STATUS.SENDING
			}
			this.fillTargetId(msgInfo, this.chat.targetId);
			this.chatStore.insertMessage(msgInfo, this.chat);
			this.moveChatToTop();
			file.msgInfo = msgInfo;
			file.chat = this.chat;
			let chat = this.chat;
			this.getImageSize(file).then(size => {
				data.width = size.width;
				data.height = size.height;
				msgInfo.content = JSON.stringify(data)
				this.chatStore.updateMessage(msgInfo, chat);
				this.scrollToBottom();
			})
		},
		onFileSuccess(url, file) {
			let data = {
				name: file.name,
				size: file.size,
				url: url
			}
			let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
			msgInfo.content = JSON.stringify(data);
			msgInfo.receipt = this.isReceipt
			this.sendMessageRequest(msgInfo).then((m) => {
				msgInfo.id = m.id;
				msgInfo.status = m.status;
				this.isReceipt = false;
				this.refreshPlaceHolder();
				this.chatStore.updateMessage(msgInfo, file.chat);
			}).catch(() => {
				msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
				this.chatStore.updateMessage(msgInfo, file.chat);
			})
		},
		onFileFail(e, file) {
			let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
			msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
			this.chatStore.updateMessage(msgInfo, file.chat);
		},
		onFileBefore(file) {
			if (this.isBanned) {
				this.showBannedTip();
				return;
			}
			let url = URL.createObjectURL(file);
			let data = {
				name: file.name,
				size: file.size,
				url: url
			}
			let msgInfo = {
				tmpId: this.generateId(),
				sendId: this.mine.id,
				content: JSON.stringify(data),
				sendTime: new Date().getTime(),
				selfSend: true,
				type: this.$enums.MESSAGE_TYPE.FILE,
				readedCount: 0,
				status: this.$enums.MESSAGE_STATUS.SENDING
			}
			this.fillTargetId(msgInfo, this.chat.targetId);
			this.chatStore.insertMessage(msgInfo, this.chat);
			this.moveChatToTop();
			file.msgInfo = msgInfo;
			file.chat = this.chat;
		},
		onCloseSide() {
			this.showSide = false;
			this.showUserSide = false;
		},
		onScrollToTop() {
			this.showMinIdx = this.showMinIdx > 10 ? this.showMinIdx - 10 : 0;
		},
		onScroll(e) {
			let scrollElement = e.target
			let scrollTop = scrollElement.scrollTop
			if (scrollTop < 30) {
				this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
				this.isInBottom = false;
			}
			if (scrollTop + scrollElement.clientHeight >= scrollElement.scrollHeight - 30) {
				this.isInBottom = true;
				this.newMessageSize = 0;
			}
		},
		showEmotionBox() {
			let width = this.$refs.emotion.offsetWidth;
			let left = this.$elm.fixLeft(this.$refs.emotion);
			let top = this.$elm.fixTop(this.$refs.emotion);
			this.$refs.emoBox.open({
				x: left + width / 2,
				y: top
			})
		},
		onEmotion(emoText) {
			this.$refs.chatInputEditor.insertEmoji(emoText);
		},
		showRecordBox() {
			this.showRecord = true;
		},
		closeRecordBox() {
			this.showRecord = false;
		},
		showPrivateVideo(mode) {
			if (this.isBanned) {
				this.showBannedTip();
				return;
			}
			let rtcInfo = {
				mode: mode,
				isHost: true,
				friend: this.friend,
			}
			this.$eventBus.$emit("openPrivateVideo", rtcInfo);
		},
		onGroupVideo() {
			if (this.isBanned) {
				this.showBannedTip();
				return;
			}
			let ids = [this.mine.id];
			let maxChannel = this.configStore.webrtc.maxChannel;
			this.$refs.rtcSel.open(maxChannel, ids, ids, []);
		},
		onInviteOk(members) {
			if (members.length < 2) {
				return;
			}
			let userInfos = [];
			members.forEach(m => {
				userInfos.push({
					id: m.userId,
					nickName: m.showNickName,
					headImage: m.headImage,
					isCamera: false,
					isMicroPhone: true
				})
			})
			let rtcInfo = {
				isHost: true,
				groupId: this.group.id,
				inviterId: this.mine.id,
				userInfos: userInfos
			}
			this.$eventBus.$emit("openGroupVideo", rtcInfo);
		},
		showHistoryBox() {
			this.showHistory = true;
		},
		closeHistoryBox() {
			this.showHistory = false;
		},
		onSendRecord(data) {
			if (this.isBanned) {
				this.showBannedTip();
				return;
			}
			let msgInfo = {
				tmpId: this.generateId(),
				content: JSON.stringify(data),
				type: this.$enums.MESSAGE_TYPE.AUDIO,
				receipt: this.isReceipt
			}
			this.fillTargetId(msgInfo, this.chat.targetId);
			const chat = this.chat;
			let tmpMessage = this.buildTmpMessage(msgInfo);
			this.chatStore.insertMessage(tmpMessage, chat);
			this.moveChatToTop();
			this.sendMessageRequest(msgInfo).then(m => {
				tmpMessage.id = m.id;
				tmpMessage.status = m.status;
				this.chatStore.updateMessage(tmpMessage, chat);
				this.moveChatToTop();
				this.$refs.chatInputEditor.focus();
				this.scrollToBottom();
				this.showRecord = false;
				this.isReceipt = false;
				this.refreshPlaceHolder();
			}).catch(() => {
				tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
				this.chatStore.updateMessage(tmpMessage, this.chat);
			})
		},
		fillTargetId(msgInfo, targetId) {
			if (this.chat.type == "GROUP") {
				msgInfo.groupId = targetId;
			} else {
				msgInfo.recvId = targetId;
			}
		},
		notifySend() {
			this.$refs.chatInputEditor.submit();
		},
		async sendMessage(fullList) {
			this.resetEditor();
			console.log(fullList);
			this.readedMessage();
			if (this.isBanned) {
				this.showBannedTip();
				return;
			}
			let sendText = this.isReceipt ? "【回执消息】" : "";
			fullList.forEach(async msg => {
				switch (msg.type) {
					case "text":
						await this.sendTextMessage(sendText + msg.content, msg.atUserIds);
						break;
					case "image":
						await this.sendImageMessage(msg.content.file);
						break;
					case "file":
						await this.sendFileMessage(msg.content.file);
						break;
				}
			})
		},
		sendImageMessage(file) {
			return new Promise((resolve, reject) => {
				this.onImageBefore(file);
				let formData = new FormData()
				formData.append('file', file)
				this.$http.post("/image/upload?isPermanent=false", formData, {
					headers: {
						'Content-Type': 'multipart/form-data'
					}
				}).then((data) => {
					this.onImageSuccess(data, file);
					resolve();
				}).catch((res) => {
					this.onImageFail(res, file);
					reject();
				})
				this.$nextTick(() => this.$refs.chatInputEditor.focus());
				this.scrollToBottom();
			});
		},
		sendTextMessage(sendText, atUserIds) {
			return new Promise((resolve, reject) => {
				if (!sendText.trim()) {
					reject();
				}
				let msgInfo = {
					tmpId: this.generateId(),
					content: sendText,
					type: this.$enums.MESSAGE_TYPE.TEXT
				}
				this.fillTargetId(msgInfo, this.chat.targetId);
				if (this.chat.type == "GROUP") {
					msgInfo.atUserIds = atUserIds;
					msgInfo.receipt = this.isReceipt;
				}
				this.lockMessage = true;
				const chat = this.chat;
				let tmpMessage = this.buildTmpMessage(msgInfo);
				this.chatStore.insertMessage(tmpMessage, chat);
				this.moveChatToTop();
				this.sendMessageRequest(msgInfo).then((m) => {
					tmpMessage.id = m.id;
					tmpMessage.status = m.status;
					tmpMessage.content = m.content;
					this.chatStore.updateMessage(tmpMessage, chat);
				}).catch(() => {
					tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
					this.chatStore.updateMessage(tmpMessage, chat);
				}).finally(() => {
					this.isReceipt = false;
					resolve();
				});
			});
		},
		sendFileMessage(file) {
			return new Promise((resolve, reject) => {
				let check = this.$refs.fileUpload.beforeUpload(file);
				if (check) {
					this.$refs.fileUpload.onFileUpload({ file });
				}
			})
		},
		onResendMessage(msgInfo) {
			if (msgInfo.type != this.$enums.MESSAGE_TYPE.TEXT) {
				this.$message.error("该消息不支持自动重新发送，建议手动重新发送")
				return;
			}
			const chat = this.chat;
			this.chatStore.deleteMessage(msgInfo, chat);
			msgInfo.tmpId = this.generateId();
			let tmpMessage = this.buildTmpMessage(msgInfo);
			this.chatStore.insertMessage(tmpMessage, chat);
			this.moveChatToTop();
			this.sendMessageRequest(msgInfo).then(m => {
				tmpMessage.id = m.id;
				tmpMessage.status = m.status;
				tmpMessage.content = m.content;
				this.chatStore.updateMessage(tmpMessage, chat);
			}).catch(() => {
				tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
				this.chatStore.updateMessage(tmpMessage, chat);
			}).finally(() => {
				this.scrollToBottom();
			});
		},
		deleteMessage(msgInfo) {
			this.$confirm('确认删除消息?', '删除消息', {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning'
			}).then(() => {
				this.chatStore.deleteMessage(msgInfo, this.chat);
			});
		},
		recallMessage(msgInfo) {
			this.$confirm('确认撤回消息?', '撤回消息', {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning'
			}).then(() => {
				let url = `/message/${this.chat.type.toLowerCase()}/recall/${msgInfo.id}`
				this.$http({
					url: url,
					method: 'delete'
				}).then((m) => {
					this.$message.success("消息已撤回");
					m.selfSend = true;
					this.chatStore.recallMessage(m, this.chat);
				})
			});
		},
		readedMessage() {
			if (this.chat.unreadCount > 0) {
				if (this.isGroup) {
					var url = `/message/group/readed?groupId=${this.chat.targetId}`
				} else {
					url = `/message/private/readed?friendId=${this.chat.targetId}`
				}
				this.$http({
					url: url,
					method: 'put'
				}).then(() => {})
				this.chatStore.resetUnreadCount(this.chat)
			}
		},
		loadReaded(fId) {
			this.$http({
				url: `/message/private/maxReadedId?friendId=${fId}`,
				method: 'get'
			}).then((id) => {
				this.chatStore.readedMessage({
					friendId: fId,
					maxId: id
				});
			});
		},
		loadGroup(groupId) {
			this.$http({
				url: `/group/find/${groupId}`,
				method: 'get'
			}).then((group) => {
				this.group = group;
				this.chatStore.updateChatFromGroup(group);
				this.groupStore.updateGroup(group);
			});

			this.$http({
				url: `/group/members/${groupId}`,
				method: 'get'
			}).then((groupMembers) => {
				this.groupMembers = groupMembers;
			});
		},
		updateFriendInfo() {
			if (this.isFriend) {
				let friend = JSON.parse(JSON.stringify(this.friend));
				friend.headImage = this.userInfo.headImageThumb;
				friend.nickName = this.userInfo.nickName;
				friend.showNickName = friend.remarkNickName ? friend.remarkNickName : friend.nickName;
				this.chatStore.updateChatFromFriend(friend);
				this.friendStore.updateFriend(friend);
			} else {
				this.chatStore.updateChatFromUser(this.userInfo);
			}
		},
		async loadFriend(friendId) {
			try {
				this.labelOptionsLoading = true;
				const userInfo = await this.$http({
					url: `/user/find/${friendId}`,
					method: 'GET'
				});
				this.userInfo = userInfo;
				if (userInfo.lastLoginIp) {
					const location = await this.getIpLocation(userInfo.lastLoginIp);
					this.ipLocation = location;
				}
				this.updateFriendInfo();
				await this.loadLabelOptions(friendId);
				this.selectedLabels = [];
				if (userInfo.labelIds) {
					this.selectedLabels = userInfo.labelIds
						.split(',')
						.map(id => String(id.trim()))
						.filter(id => id);
				}
				await this.loadAllGroupOptions(friendId);
				this.$nextTick(() => {
					if (this.userInfo.groupIds) {
						this.selectedGroup = String(this.userInfo.groupIds);
					} else if (this.userInfo.groupNames && this.userInfo.groupNames.length > 0) {
						const currentGroupName = this.userInfo.groupNames[0];
						const matchedGroup = this.groupOptions.find(item => item.label === currentGroupName);
						if (matchedGroup) {
							this.selectedGroup = matchedGroup.value;
						}
					}
				});
			} catch (error) {
				console.error('加载用户信息失败:', error);
			} finally {
				this.labelOptionsLoading = false;
			}
		},
		showName(msgInfo) {
			if (!msgInfo) {
				return ""
			}
			if (this.isGroup) {
				let member = this.groupMembers.find((m) => m.userId == msgInfo.sendId);
				return member ? member.showNickName : msgInfo.sendNickName || "";
			} else {
				return msgInfo.selfSend ? this.mine.nickName : this.chat.showName
			}
		},
		headImage(msgInfo) {
			if (this.isGroup) {
				let member = this.groupMembers.find((m) => m.userId == msgInfo.sendId);
				return member ? member.headImage : "";
			} else {
				return msgInfo.sendId == this.mine.id ? this.mine.headImageThumb : this.chat.headImage
			}
		},
		resetEditor() {
			this.$nextTick(() => {
				this.$refs.chatInputEditor.clear();
				this.$refs.chatInputEditor.focus();
			});
		},
		scrollToBottom() {
			this.$nextTick(() => {
				let div = document.getElementById("chatScrollBox");
				div.scrollTop = div.scrollHeight;
			});
		},
		refreshPlaceHolder() {
			if (this.isReceipt) {
				this.placeholder = "【回执消息】"
			} else if (this.$refs.editBox && this.$refs.editBox.innerHTML) {
				this.placeholder = ""
			} else {
				this.placeholder = "聊点什么吧~";
			}
		},
		sendMessageRequest(msgInfo) {
			return new Promise((resolve, reject) => {
				this.reqQueue.push({ msgInfo, resolve, reject });
				this.processReqQueue();
			})
		},
		processReqQueue() {
			if (this.reqQueue.length && !this.isSending) {
				this.isSending = true;
				const reqData = this.reqQueue.shift();
				this.$http({
					url: this.messageAction,
					method: 'post',
					data: reqData.msgInfo
				}).then((res) => {
					reqData.resolve(res)
				}).catch((e) => {
					reqData.reject(e)
				}).finally(() => {
					this.isSending = false;
					this.processReqQueue();
				})
			}
		},
		showBannedTip() {
			let msgInfo = {
				tmpId: this.generateId(),
				sendId: this.mine.id,
				sendTime: new Date().getTime(),
				type: this.$enums.MESSAGE_TYPE.TIP_TEXT
			}
			if (this.chat.type == "PRIVATE") {
				msgInfo.recvId = this.mine.id
				msgInfo.content = "该用户已被管理员封禁,原因:" + this.userInfo.reason
			} else {
				msgInfo.groupId = this.group.id;
				msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason
			}
			this.chatStore.insertMessage(msgInfo, this.chat);
		},
		buildTmpMessage(msgInfo) {
			let message = JSON.parse(JSON.stringify(msgInfo));
			message.sendId = this.mine.id;
			message.sendTime = new Date().getTime();
			message.status = this.$enums.MESSAGE_STATUS.SENDING;
			message.selfSend = true;
			if (this.isGroup) {
				message.readedCount = 0;
			}
			return message;
		},
		getImageSize(file) {
			return new Promise((resolve, reject) => {
				const reader = new FileReader();
				reader.onload = function(event) {
					const img = new Image();
					img.onload = function() {
						resolve({ width: img.width, height: img.height });
					};
					img.onerror = function() {
						reject(new Error('无法加载图片'));
					};
					img.src = event.target.result;
				};
				reader.onerror = function() {
					reject(new Error('无法读取文件'));
				};
				reader.readAsDataURL(file);
			});
		},
		generateId() {
			const id = String(new Date().getTime()) + String(Math.floor(Math.random() * 1000));
			if (this.maxTmpId > id) {
				return this.generateId();
			}
			this.maxTmpId = id;
			return id;
		},
		showChangeCustomerBox() {
			this.showChangeCustomer = true;
		},
		closeChangeCustomerBox() {
			this.showChangeCustomer = false;
		},
		onTransferSuccess() {
			this.$message.success('已成功转接客服');
		},
		async getIpLocation(ip) {
			if (!ip) return '';
			try {
				const response = await fetch(`http://ip-api.com/json/${ip}?lang=zh-CN`);
				const data = await response.json();
				if (data && data.status === 'success') {
					let location = [];
					if (data.country) location.push(data.country);
					if (data.regionName) location.push(data.regionName);
					if (data.city) location.push(data.city);
					return location.join(' ');
				}
				return '';
			} catch (error) {
				console.error('获取IP地理位置失败:', error);
				try {
					const backupResponse = await fetch(`https://whois.pconline.com.cn/ipJson.jsp?ip=${ip}&json=true`);
					const text = await backupResponse.text();
					const data = JSON.parse(text);
					if (data) {
						let location = [];
						if (data.pro) location.push(data.pro);
						if (data.city) location.push(data.city);
						if (data.addr) location.push(data.addr);
						return location.join(' ');
					}
				} catch (backupError) {
					console.error('备用API也失败了:', backupError);
				}
				return '';
			}
		},
		formatIpDisplay(ip, location) {
			if (!ip) return '';
			return location ? `${ip}（${location}）` : ip;
		},
		handleAddUserGroup() {
			this.$prompt('请输入分组名称', '添加分组', {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
			}).then(({ value }) => {
				if (!value || !value.trim()) {
					this.$message.warning('分组名称不能为空');
					return;
				}
			}).catch(() => {});
		},
		loadAllGroupOptions(friendId) {
			return this.$http.get(`/user/group/${friendId}`).then(res => {
				console.log('分组数据:', res);
				if (res.groupList && Array.isArray(res.groupList)) {
					this.groupOptions = res.groupList.map(item => ({
						value: item.id,
						label: item.groupName
					}));
				} else {
					this.groupOptions = [];
				}
				return res;
			}).catch(error => {
				console.error('加载分组选项失败:', error);
				this.groupOptions = [];
			});
		},
		handleSelectUserGroup(groupId) {
			if (groupId === '__CREATE_NEW__') {
				this.$prompt('请输入新分组名称', '创建分组', {
					confirmButtonText: '确定',
					cancelButtonText: '取消',
				}).then(({ value }) => {
					if (!value.trim()) return
					this.createNewGroup(value.trim())
				})
			} else {
				this.doSaveGroup(groupId)
			}
		},
		doSaveGroup(groupId) {
			this.$http.post('/user/group/save', {
				userId: this.userInfo.id,
				groupIds: String(groupId)
			}).then(() => {
				console.log('res',groupId)
				this.$message.success('分组设置成功')
				this.loadFriend(this.chat.targetId)
			})
		},
		loadLabelOptions(userId) {
			this.labelOptionsLoading = true;
			return this.$http.get(`/user/label/${userId}`).then(res => {
				console.log('标签选项数据:', res);
				if (res && Array.isArray(res.labelList)) {
					this.labelOptions = res.labelList.map(item => ({
						value: item.id,
						label: item.labelName || item.name
					}));
				} else {
					this.labelOptions = [];
				}
				return res;
			}).catch(error => {
				console.error('加载标签选项失败:', error);
				this.labelOptions = [];
			}).finally(() => {
				this.labelOptionsLoading = false;
			});
		},
		getLabelName(labelId) {
			const label = this.labelOptions.find(item => item.value == labelId);
			return label ? label.label : '';
		},
		removeLabel(labelId) {
			const index = this.selectedLabels.indexOf(labelId);
			if (index > -1) {
				this.selectedLabels.splice(index, 1);
				this.saveLabels();
			}
		},
		handleSelectLabels(labelIds) {
			const ids = Array.isArray(labelIds) ? labelIds : [labelIds];
			this.selectedLabels = [...new Set(ids.map(id => String(id)))];
			this.saveLabels();
		},
		saveLabels() {
			const uniqueLabels = [...new Set(this.selectedLabels)];
			this.$http.post('/user/label/save', {
				userId: this.userInfo.id,
				labelIds: uniqueLabels.join(',')
			}).then(() => {
				this.$message.success('标签设置成功');
				this.loadFriend(this.chat.targetId);
			}).catch(() => {
				this.$message.error('标签设置失败');
			});
		},

		// ==================== 快捷回复相关 ====================
		// 打开快捷回复并加载列表
		openQuickReplyBox() {
			this.showQuickReplyBox = true;
			this.loadQuickReplyList();
		},
		// 关闭
		closeQuickReplyBox() {
			this.showQuickReplyBox = false;
		},
		loadQuickReplyList() {
			this.quickLoading = true;
			// 传当前登录用户的 ID 给后端
			this.$http.get("/quick/reply/list", {
				params: {
				userId: this.mine.id
				}
			})
			.then(res => {
				if (res && Array.isArray(res)) {
					this.quickReplyList = res;
				} else {
					this.quickReplyList = [];
				}
			})
			.catch(() => {
				this.quickReplyList = [];
			})
			.finally(() => {
				this.quickLoading = false;
			});
		},
		// 选择快捷语，插入输入框并发送
		// selectQuickReply(item) {
		// 	if (!item || !item.replyContent) return;
		// 	console.log('item',item)
		// 	// 先清空输入框
		// 	this.$refs.chatInputEditor.clear();
			
		// 	// 插入快捷回复内容（如果组件有 insertText 或类似方法）
		// 	// 方法1：如果 ChatInput 有 insertText 方法
		// 	if (this.$refs.chatInputEditor.insertText) {
		// 		this.$refs.chatInputEditor.insertText(item.replyContent);
		// 	} 
		// 	// 方法2：如果只能通过设置 content 属性
		// 	else {
		// 		this.$refs.chatInputEditor.content = item.replyContent;
		// 	}
			
		// 	// 关闭弹窗
		// 	this.showQuickReplyBox = false;
		// 	const fullList = [
		// 		{
		// 			content: item.replyContent,
		// 			type: item.replyType == 0 ? 'text' : 'image'
		// 		}
		// 	];
		// 	// 延迟一点确保内容已设置，然后自动发送
		// 	this.$nextTick(() => {
		// 		setTimeout(() => {
		// 			this.sendMessage(fullList);
		// 			// this.notifySend();
		// 		}, 50);
		// 	});
		// }
		// 选择快捷语，插入输入框并发送
		selectQuickReply(item) {
			if (!item || !item.replyContent) return;
			console.log('快捷回复数据:', item);
			
			// 关闭弹窗
			this.showQuickReplyBox = false;
			
			this.$nextTick(() => {
				// 根据 replyType 判断消息类型
				if (item.replyType === 0) {
					// 文本消息
					this.sendQuickTextMessage(item.replyContent);
				} else if (item.replyType === 1) {
					// 图片消息（URL格式）
					this.sendQuickImageMessage(item.replyContent);
				} else {
					// 默认当作文本处理
					this.sendQuickTextMessage(item.replyContent);
				}
			});
		},

		// 发送快捷文本消息
		sendQuickTextMessage(content) {
			let sendText = this.isReceipt ? "【回执消息】" : "";
			let msgInfo = {
				tmpId: this.generateId(),
				content: sendText + content,
				type: this.$enums.MESSAGE_TYPE.TEXT
			};
			
			this.fillTargetId(msgInfo, this.chat.targetId);
			
			if (this.chat.type == "GROUP") {
				msgInfo.atUserIds = [];
				msgInfo.receipt = this.isReceipt;
			}
			
			const chat = this.chat;
			let tmpMessage = this.buildTmpMessage(msgInfo);
			this.chatStore.insertMessage(tmpMessage, chat);
			this.moveChatToTop();
			
			this.sendMessageRequest(msgInfo).then((m) => {
				tmpMessage.id = m.id;
				tmpMessage.status = m.status;
				tmpMessage.content = m.content;
				this.chatStore.updateMessage(tmpMessage, chat);
				this.scrollToBottom();
				this.$refs.chatInputEditor.clear();
			}).catch(() => {
				tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
				this.chatStore.updateMessage(tmpMessage, chat);
			}).finally(() => {
				this.isReceipt = false;
				this.refreshPlaceHolder();
			});
		},

		// 发送快捷图片消息（URL格式）
		sendQuickImageMessage(imageUrl) {
			// 构造图片消息数据
			let imageData = {
				originUrl: imageUrl,
				thumbUrl: imageUrl,  // 缩略图使用相同URL，或者后端会生成缩略图
				width: 0,            // 如果不知道尺寸，设为0
				height: 0
			};
			
			let msgInfo = {
				tmpId: this.generateId(),
				sendId: this.mine.id,
				content: JSON.stringify(imageData),
				sendTime: new Date().getTime(),
				selfSend: true,
				type: this.$enums.MESSAGE_TYPE.IMAGE,
				readedCount: 0,
				status: this.$enums.MESSAGE_STATUS.SENDING,
				receipt: this.isReceipt
			};
			
			this.fillTargetId(msgInfo, this.chat.targetId);
			
			const chat = this.chat;
			
			// 先插入临时消息显示
			this.chatStore.insertMessage(msgInfo, chat);
			this.moveChatToTop();
			this.scrollToBottom();
			
			// 发送消息
			this.sendMessageRequest(msgInfo).then((m) => {
				msgInfo.id = m.id;
				msgInfo.status = m.status;
				this.isReceipt = false;
				this.chatStore.updateMessage(msgInfo, chat);
				this.$refs.chatInputEditor.clear();
				this.refreshPlaceHolder();
			}).catch(() => {
				msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
				this.chatStore.updateMessage(msgInfo, chat);
			});
		},
	},
	computed: {
		mine() {
			return this.userStore.userInfo;
		},
		isFriend() {
			return this.friendStore.isFriend(this.userInfo.id);
		},
		friend() {
			return this.friendStore.findFriend(this.userInfo.id)
		},
		title() {
			let title = this.chat.showName;
			if (this.chat.type == "GROUP") {
				let size = this.groupMembers.filter(m => !m.quit).length;
				title += `(${size})`;
			}
			return title;
		},
		messageAction() {
			return `/message/${this.chat.type.toLowerCase()}/send`;
		},
		unreadCount() {
			return this.chat.unreadCount;
		},
		showMessages() {
			return this.chat.messages.slice(this.showMinIdx)
		},
		messageSize() {
			if (!this.chat || !this.chat.messages) {
				return 0;
			}
			return this.chat.messages.length;
		},
		isBanned() {
			return (this.chat.type == "PRIVATE" && this.userInfo.isBanned) ||
				(this.chat.type == "GROUP" && this.group.isBanned)
		},
		memberSize() {
			return this.groupMembers.filter(m => !m.quit).length;
		},
		isGroup() {
			return this.chat.type == 'GROUP';
		},
		isPrivate() {
			return this.chat.type == 'PRIVATE';
		},
		loading() {
			return this.chatStore.loading;
		},
		targetUserId() {
			if (this.chat.type === 'PRIVATE') {
				return this.chat.targetId;
			} else if (this.chat.type === 'GROUP') {
				return this.group.id;
			}
			return 0;
		},
		availableLabelOptions() {
			return this.labelOptions.filter(item => 
			!this.selectedLabels.includes(String(item.value))
			);
		}
	},
	watch: {
		chat: {
			handler(newChat, oldChat) {
				if (newChat.targetId > 0 && (!oldChat || newChat.type != oldChat.type ||
						newChat.targetId != oldChat.targetId)) {
					this.userInfo = {}
					this.group = {};
					this.groupMembers = [];
					if (this.chat.type == "GROUP") {
						this.loadGroup(this.chat.targetId);
					} else {
						this.loadFriend(this.chat.targetId);
						this.loadReaded(this.chat.targetId)
					}
					this.scrollToBottom();
					this.showSide = false;
					this.readedMessage()
					let size = this.chat.messages.length;
					this.showMinIdx = size > 30 ? size - 30 : 0;
					this.resetEditor();
					this.isReceipt = false;
					this.maxTmpId = 0;
					this.refreshPlaceHolder();
				}
			},
			immediate: true
		},
		messageSize: {
			handler(newSize, oldSize) {
				if (newSize > oldSize) {
					let lastMessage = this.chat.messages[newSize - 1];
					if (lastMessage && this.$msgType.isNormal(lastMessage.type)) {
						if (this.isInBottom || lastMessage.selfSend) {
							this.scrollToBottom();
						} else {
							this.newMessageSize++;
						}
					}
				}
			}
		},
		loading: {
			handler(newLoading, oldLoading) {
				if (!newLoading && this.isPrivate) {
					this.loadReaded(this.chat.targetId)
				}
			}
		}
	},
	mounted() {
		let div = document.getElementById("chatScrollBox");
		div.addEventListener('scroll', this.onScroll)
	}
}
</script>

<style lang="scss" scoped>
.chat-box {
	position: relative;
	width: 100%;
	background: #fff;

	.el-header {
		display: flex;
		justify-content: space-between;
		padding: 0 12px;
		line-height: 50px;
		font-size: var(--im-font-size-larger);
		border-bottom: var(--im-border);
		.btn-side {
			position: absolute;
			right: 20px;
			line-height: 50px;
			font-size: 20px;
			cursor: pointer;
			color: var(--im-text-color-light);
		}
	}

	.content-box {
		position: relative;
		.im-chat-main {
			padding: 0;
			background-color: #f4f5f6;
			.im-chat-box {
				>ul {
					padding: 0 20px;
					li {
						list-style-type: none;
					}
				}
			}
		}
		.scroll-to-bottom {
			text-align: right;
			position: absolute;
			right: 20px;
			bottom: 230px;
			color: var(--im-color-primary);
			font-size: var(--im-font-size);
			font-weight: 600;
			background: #eee;
			padding: 5px 15px;
			border-radius: 15px;
			cursor: pointer;
			z-index: 99;
			box-shadow: var(--im-box-shadow-light);
		}
		.im-chat-footer {
			display: flex;
			flex-direction: column;
			padding: 0;
			.chat-tool-bar {
				display: flex;
				position: relative;
				width: 100%;
				height: 36px;
				text-align: left;
				box-sizing: border-box;
				border-top: var(--im-border);
				padding: 4px 2px 2px 8px;
				>div {
					font-size: 22px;
					cursor: pointer;
					line-height: 30px;
					width: 30px;
					height: 30px;
					text-align: center;
					border-radius: 2px;
					margin-right: 8px;
					color: #999;
					transition: 0.3s;
					&.chat-tool-active {
						font-weight: 600;
						color: var(--im-color-primary);
						background-color: #ddd;
					}
				}
				>div:hover {
					color: #333;
				}
			}
			.send-content-area {
				position: relative;
				display: flex;
				flex-direction: column;
				height: 100%;
				background-color: white !important;
				.send-btn-area {
					padding: 10px;
					position: absolute;
					bottom: 4px;
					right: 6px;
				}
			}
		}
	}
	.side-box {
		border-left: var(--im-border);
	}
	.user-info-side {
		.user-info-container {
			padding: 20px;
			height: 100%;
			overflow-y: auto;
			.user-info-header {
				text-align: center;
				padding-bottom: 20px;
				border-bottom: 1px solid #eee;
				.user-avatar-large {
					width: 100px;
					height: 100px;
					border-radius: 50%;
					object-fit: cover;
					margin-bottom: 15px;
				}
				.user-nickname {
					font-size: 18px;
					font-weight: 600;
					margin: 10px 0 5px;
					color: #333;
				}
				.user-username {
					font-size: 14px;
					color: #999;
					margin: 0;
				}
			}
			.user-info-content {
				padding: 20px 0;
				.info-item {
					display: flex;
					align-items: center;
					padding: 10px 0;
					border-bottom: 1px solid #f0f0f0;
					.info-label {
						width: 70px;
						font-size: 14px;
						color: #999;
						flex-shrink: 0;
					}
					.info-value {
						flex: 1;
						font-size: 14px;
						color: #333;
						word-break: break-all;
						::v-deep .el-select {
							.el-tag {
							background-color: #4d4949 !important;
							color: #fff !important;
							border-color: #1d1b1b !important;
								.el-tag__close {
									color: #fff !important;
									&:hover {
									background-color: #333 !important;
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

// 快捷回复样式
.quick-reply-list {
	padding: 10px;
	.quick-item {
		padding: 10px 12px;
		margin-bottom: 8px;
		background: #f7f8fa;
		border-radius: 6px;
		cursor: pointer;
		transition: all 0.2s;
		border: 1px solid transparent;
		&:hover {
			background: #e6f7ff;
			border-color: #91caff;
		}
	}
	.quick-empty {
		text-align: center;
		padding: 30px 0;
		color: #999;
	}
}
</style>