<template>
  <!-- 模板完全保持不变，与原来一致 -->
  <view class=" chat-box"  id="chatBox">
<!--   <nav-bar  more @more="onShowMore">{{ title }}</nav-bar> -->
    <view class="chat-main-box"  :style="{height: chatMainHeight+'px'}" >
      <view class="chat-message" @click="switchChatTabBox('none')"  :style="{ paddingBottom: bottomPadding + 'px' }" >
        <scroll-view class="scroll-box" scroll-y="true" upper-threshold="200" @scrolltoupper="onScrollToTop"
                     :scroll-into-view="'chat-item-' + scrollMsgIdx" >
          <view v-if="chat" v-for="(msgInfo, idx) in chat.messages" :key="idx">
            <chat-message-item :ref="'message'+msgInfo.id" v-if="idx >= showMinIdx"
                               :headImage="headImage(msgInfo)" @call="onRtCall(msgInfo)" :showName="showName(msgInfo)"
                               @recall="onRecallMessage" @delete="onDeleteMessage" @copy="onCopyMessage"
                               @longPressHead="onLongPressHead(msgInfo)" @download="onDownloadFile"
                               @audioStateChange="onAudioStateChange" :id="'chat-item-' + idx" :msgInfo="msgInfo"
                               :groupMembers="groupMembers">
            </chat-message-item>
          </view>
        </scroll-view>
        <view v-if="!isInBottom" class="scroll-to-bottom" @click="onClickToBottom">
          {{ newMessageSize > 0 ?  newMessageSize+'条新消息' :'回到底部'}}
        </view>
      </view>
<!--      <view v-if="atUserIds.length > 0" class="chat-at-bar" @click="openAtBox()">-->
<!--        <view class="iconfont icon-at">&nbsp;</view>-->
<!--        <scroll-view v-if="atUserIds.length > 0" class="chat-at-scroll-box" scroll-x="true" scroll-left="120">-->
<!--          <view class="chat-at-items">-->
<!--            <view v-for="m in atUserItems" class="chat-at-item" :key="m.userId">-->
<!--              <head-image :name="m.showNickName" :url="m.headImage" size="minier"></head-image>-->
<!--            </view>-->
<!--          </view>-->
<!--        </scroll-view>-->
<!--      </view>-->
      <submit  ref="submitRef" @inputs="onInputs"  @send-message="onSendMessage" @heights="onHeights" />
    </view>
  </view>
</template>

<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import { myCache } from '../../utils/utils.js';
import submit from '@/components/chat/submit.vue';
import myVideo from "@/components/myVideo.vue";
//音频播放
const innerAudioContext = uni.createInnerAudioContext();
export default {
  components: {
    submit,
    myVideo
  },
  data() {
    return {
      chat: {},
      userInfo: {},
      group: {},
      groupMembers: [],
      isReceipt: true,
      scrollMsgIdx: 0,
      chatTabBox: 'none',
      showRecord: false,
      chatMainHeight: 0,
      bottomPadding: 50,
      keyboardHeight: 290,
      windowHeight: 1000,
      initHeight: 1000,
      atUserIds: [],
      needScrollToBottom: false,
      showMinIdx: 0,
      reqQueue: [],
      isSending: false,
      isShowKeyBoard: false,
      editorCtx: null,
      isEmpty: true,
      isFocus: false,
      isReadOnly: false,
      playingAudio: null,
      isInBottom: true,
      newMessageSize: 0
    };
  },
  computed: {
    // 映射 Vuex state
    ...mapState('chatStore', ['chats']),
    // ...mapState('userStore', ['userInfo']),

    // ...mapState('configStore', ['config']),
    // 映射 Vuex getters
    ...mapGetters('friendStore', ['findFriend']),
    ...mapGetters('chatStore', ['findChatIdx']),

    // 当前用户信息（兼容原 `mine`）
    mine() {
      return myCache('user') || {};
    },
    // 好友信息
    friend() {
      if (!this.userInfo.id) return null;
      return this.findFriend(this.userInfo.id);
    },
    // 会话标题
    title() {
      if (!this.chat) return '';
      let title = this.chat.showName;
      if (this.chat.type === 'GROUP' ) {
        const size = this.groupMembers.filter(m => !m.quit).length;
        title += `(${size})`;
      }
      return title;
    },
    // 消息发送接口 URL
    messageAction() {
      return `/api/message/${this.chat.type.toLowerCase()}/send`;
    },
    // 消息数量
    messageSize() {
      return this.chat?.messages?.length || 0;
    },
    // 未读消息数
    unreadCount() {
      return this.chat?.unreadCount || 0;
    },
    // 是否被封禁
    isBanned() {
      return (this.chat.type === 'PRIVATE' && this.userInfo.isBanned) ||
          (this.chat.type === 'GROUP' && this.group.isBanned);
    },
    // @ 成员列表
    atUserItems() {
      const items = [];
      this.atUserIds.forEach(id => {
        if (id === -1) {
          items.push({ id: -1, showNickName: '全体成员' });
        } else {
          const member = this.groupMembers.find(m => m.userId === id);
          if (member) items.push(member);
        }
      });
      return items;
    },
    // 群成员数
    memberSize() {
      return this.groupMembers.filter(m => !m.quit).length;
    },
    // 配置中的 webrtc 最大通道数（用于模板）
    webrtcMaxChannel() {
      // return this.config?.webrtc?.maxChannel || 0;
    }
  },
  watch: {
    messageSize(newSize, oldSize) {
      if (newSize > oldSize) {
        if (this.isInBottom) {
          this.scrollToBottom();
        } else {
          this.newMessageSize++;
        }
      }
    },
    unreadCount(newCount) {
      if (newCount > 0) {
        this.toReadedMessage();
      }
    }
  },
  onLoad(options) {

    // 从 Vuex state 中获取当前会话
    this.chat = this.chats[options.chatIdx];
    const size = this.messageSize;
    this.showMinIdx = size > 20 ? size - 20 : 0;
    this.toReadedMessage();

    if (this.chat.type === 'GROUP') {
      this.loadGroup(this.chat.targetId);
      uni.setNavigationBarTitle({
        title: this.chat.showName
      });
    } else {
      this.loadFriend(this.chat.targetId);
      this.loadReaded(this.chat.targetId);
      uni.setNavigationBarTitle({
        title: this.chat.showName
      });
    }
    // 激活当前会话（action）
    this.activeChat(options.chatIdx);
    this.isReceipt = true;
    this.listenKeyBoard();

    this.$nextTick(() => {
      this.windowHeight = uni.getSystemInfoSync().windowHeight;
      this.reCalChatMainHeight();
      this.scrollToBottom();
      // #ifdef H5
      this.initHeight = window.innerHeight;
      const chatBox = document.getElementById('chatBox');
      chatBox.addEventListener('touchmove', e => e.preventDefault(), { passive: false });
      // #endif
    });
  },
  onUnload() {
    this.unListenKeyboard();
  },
  onShow() {
    if (this.needScrollToBottom) {
      this.scrollToBottom();
      this.needScrollToBottom = false;
    }
	this.setBadge();
  },
  
  methods: {
    // 映射 Vuex actions
    ...mapActions('chatStore', [
      'insertMessage',
      'deleteMessage',
      'recallMessage',
      'moveTop',
      'resetUnreadCount',
      'updateChatFromGroup',
      'updateChatFromFriend',
      'updateChatFromUser',
      'readedMessage',
      'activeChat'
    ]),
    ...mapActions('friendStore', ['updateFriend']),
    ...mapActions('groupStore', ['updateGroup']),
	// 设置app角标
	setBadge() {
			 var count = myCache("massageCount")?myCache("massageCount"):-1;
			  if (count > 0) { 
				// app清除角标的数量
				//#ifdef APP-PLUS
				plus.runtime.setBadgeNumber(count);
				//#endif
				
			  } else { 
				// app清除角标的数量
				//#ifdef APP-PLUS
				plus.runtime.setBadgeNumber(-1);
				//#endif
			  }
	},
    // 以下是原业务方法，只需将 `this.chatStore.xxx` 替换为映射后的方法名
    onRecorderInput() {
      this.showRecord = true;
      this.switchChatTabBox('none');
    },
    onKeyboardInput() {
      this.showRecord = false;
      this.switchChatTabBox('none');
    },
    onSendRecord(data) {
      if (this.isBanned) {
        this.showBannedTip();
        return;
      }
      const msgInfo = {
        content: JSON.stringify(data),
        type: this.$enums.MESSAGE_TYPE.AUDIO,
        receipt: this.isReceipt
      };
      this.fillTargetId(msgInfo, this.chat.targetId);
      this.sendMessageRequest(msgInfo).then(res => {
        var m=res.data.data;
        m.selfSend = true;
        this.insertMessage({ msgInfo: m, chatInfo: this.chat });
        this.moveChatToTop();
        this.scrollToBottom();
        this.isReceipt = true;
      });
    },
    onInputs(data) {
      if (this.isBanned) {
        this.showBannedTip();
        return;
      }
      const { message, type } = data;
      if (type === 'txt') {
        // 文本消息直接发送
        const msgInfo = {
          content: message,
          type: this.$enums.MESSAGE_TYPE.TEXT,
          receipt: true, // 可根据需要调整
        };
        this.fillTargetId(msgInfo, this.chat.targetId);
        this.sendMessageRequest(msgInfo).then(m => {
          m.selfSend = true;
          this.insertMessage({ msgInfo: m, chatInfo: this.chat });
          this.moveChatToTop();
          this.scrollToBottom();
        });
      } else if (type === 'image' || type === 'video' || type === 'audio') {
        // 图片/视频/语音需要先上传，这里直接调用现有的上传逻辑
        // 因为 submit 组件已经做了文件选择，只需用 message 路径
        this.uploadAndSend(message, type);
      } else {
        // 其他类型（如定位）可按需扩展
        console.warn('未处理的消息类型:', type);
      }
    },

    /**
     * 接收 submit 组件发出的高度变化事件
     * @param {number} height - submit 组件总高度（px）
     */
    onHeights(height) {
      this.bottomPadding = height;
      // 若需同步滚动到底部，可在此调用
      this.scrollToBottom();
    },

    // 扩展的上传发送方法（类似 submit 中原有的 uploadFile 逻辑）
    uploadAndSend(filePath, type) {
      // 复用原有上传逻辑，例如 onUploadImageBefore 等
      // 此处简化，实际应调用统一的发送流程
      // ...
    },
    onRtCall(msgInfo) {
      if (msgInfo.type === this.$enums.MESSAGE_TYPE.ACT_RT_VOICE) {
        this.onPriviteVoice();
      } else if (msgInfo.type === this.$enums.MESSAGE_TYPE.ACT_RT_VIDEO) {
        this.onPriviteVideo();
      }
    },
    onPriviteVideo() {
      const friendInfo = encodeURIComponent(JSON.stringify(this.friend));
      uni.navigateTo({
        url: `/pages/chat/chat-private-video?mode=video&friend=${friendInfo}&isHost=true`
      });
    },
    onPriviteVoice() {
      const friendInfo = encodeURIComponent(JSON.stringify(this.friend));
      uni.navigateTo({
        url: `/pages/chat/chat-private-video?mode=voice&friend=${friendInfo}&isHost=true`
      });
    },
    onGroupVideo() {
      const ids = [this.info.userid];
      this.$refs.selBox.init(ids, ids, []);
      this.$refs.selBox.open();
    },
    onInviteOk(ids) {
      if (ids.length < 2) return;
      const users = ids.map(id => {
        const m = this.groupMembers.find(m => m.userId === id);
        return {
          id: m.userId,
          nickName: m.showNickName,
          headImage: m.headImage,
          isCamera: false,
          isMicroPhone: true
        };
      });
      const groupId = this.group.id;
      const inviterId = this.info.userid;
      const userInfos = encodeURIComponent(JSON.stringify(users));
      uni.navigateTo({
        url: `/pages/chat/chat-group-video?groupId=${groupId}&isHost=true&inviterId=${inviterId}&userInfos=${userInfos}`
      });
    },
    moveChatToTop() {
      const idx = this.findChatIdx(this.chat);
      if (idx >= 0) {
        this.moveTop(idx);
      }
    },
    switchReceipt() {
      this.isReceipt = !this.isReceipt;
    },
    openAtBox() {
      this.$refs.atBox.init(this.atUserIds);
      this.$refs.atBox.open();
    },
    onAtComplete(atUserIds) {
      this.atUserIds = atUserIds;
    },
    onLongPressHead(msgInfo) {
      if (!msgInfo.selfSend && this.chat.type === 'GROUP' && this.atUserIds.indexOf(msgInfo.sendId) < 0) {
        this.atUserIds.push(msgInfo.sendId);
      }
    },
    headImage(msgInfo) {
      if (this.chat.type === 'GROUP') {
        const member = this.groupMembers.find(m => m.userId === msgInfo.sendId);
        return member ? member.headImage : '';
      } else {
        return msgInfo.selfSend ? this.mine.avatar : this.chat.headImage;
      }
    },
    showName(msgInfo) {
      if (this.chat.type === 'GROUP') {
        const member = this.groupMembers.find(m => m.userId === msgInfo.sendId);
        return member ? member.showNickName : '';
      } else {
        return msgInfo.selfSend ? this.mine.nickName : this.chat.showName;
      }
    },
    onSendMessage(payload) {
      if (this.isBanned) {
        this.showBannedTip();
        return;
      }
      const { content, type } = payload;
      // 文本消息直接发送
      if (type === 'txt') {
        // 仿照 sendTextMessage 构造 msgInfo，但无需从 editor 获取内容
        // 注意：这里需要处理 @ 和回执，可从当前实例获取 this.atUserIds 和 this.isReceipt
        let sendText = content.trim();
        if (!sendText && this.atUserIds.length === 0) {
          return uni.showToast({ title: '不能发送空白信息', icon: 'none' });
        }
        // const receiptText = this.isReceipt ? '【回执消息】' : '';
        const atText = this.createAtText(); // 沿用已有的 createAtText 方法
        const msgInfo = {
          content: this.html2Escape(sendText) + atText,
          atUserIds: this.atUserIds,
          receipt: this.isReceipt,
          type: this.$enums.MESSAGE_TYPE.TEXT
        };
        // 清空 @ 和回执状态
        this.atUserIds = [];
        this.isReceipt = true;
        this.fillTargetId(msgInfo, this.chat.targetId);
        this.sendMessageRequest(msgInfo).then(m => {
          m.selfSend = true;
          this.insertMessage({ msgInfo: m, chatInfo: this.chat });
          this.moveChatToTop();
          this.scrollToBottom();
        });
      }
      // 图片消息
      else if (type === 'image') {
        // 直接使用现有的图片上传发送逻辑，可调用 onUploadImageBefore 的流程
        // 但 onUploadImageBefore 需要 file 对象，这里 content 是文件路径
        // 可以模拟一个 file 对象，或直接调用发送图片的封装方法
        this.uploadAndSendImage(content);
      }
      // 视频消息
      else if (type === 'video') {
        this.uploadAndSendVideo(content);
      }
      // 音频消息
      else if (type === 'audio') {
        // content 为 { voice, time }
        this.uploadAndSendAudio(content);
      }
      // 其他类型可根据需要扩展
      else {
        uni.showToast({ title: '暂不支持该类型', icon: 'none' });
      }
    },
    /**
     * 上传并发送图片
     * @param {string} filePath - 图片本地路径
     */
    uploadAndSendImage(filePath) {
      if (this.isBanned) {
        this.showBannedTip();
        return;
      }
      // 构造临时消息（模拟文件对象）
      const data = { originUrl: filePath, thumbUrl: filePath };

      const msgInfo = {
        id: 0,
        tmpId: this.generateId(),
        sendId: this.mine.userid,
        content: JSON.stringify(data),
        sendTime: Date.now(),
        selfSend: true,
        type: this.$enums.MESSAGE_TYPE.IMAGE,
        readedCount: 0,
        loadStatus: 'loading',
        status: this.$enums.MESSAGE_STATUS.UNSEND,
        receipt: this.isReceipt
      };

      this.fillTargetId(msgInfo, this.chat.targetId);
      // 先插入消息（占位）
      this.insertMessage({ msgInfo, chatInfo: this.chat });
      this.moveChatToTop();
      this.scrollToBottom();

      // 开始上传
      const token = uni.getStorageSync('token');
      uni.uploadFile({
        url: uni.$http.baseUrl + '/api/image/upload',
        filePath: filePath,
        name: 'file',
        header: { Authorization: token },
        success: (res) => {
          const result = JSON.parse(res.data);
          if (result.data && result.data.originUrl) {
            // 更新消息内容
            msgInfo.content = JSON.stringify(result.data);
            msgInfo.receipt = this.isReceipt;
            // 发送消息请求
            this.sendMessageRequest(msgInfo).then(m => {
              msgInfo.loadStatus = 'ok';
              msgInfo.id = m.id;
              // 再次插入更新消息
              this.insertMessage({ msgInfo, chatInfo: this.chat });
              this.isReceipt = true; // 重置回执状态（可根据需求调整）
            });
          } else {
            // 上传失败
            msgInfo.loadStatus = 'fail';
            this.insertMessage({ msgInfo, chatInfo: this.chat });
            uni.showToast({ title: result.msg || '图片上传失败', icon: 'none' });
          }
        },
        fail: (err) => {
          msgInfo.loadStatus = 'fail';
          this.insertMessage({ msgInfo, chatInfo: this.chat });
          uni.showToast({ title: '图片上传失败', icon: 'none' });
        }
      });
    },

    /**
     * 上传并发送视频
     * @param {string} filePath - 视频本地路径
     */
    uploadAndSendVideo(filePath) {
      // 视频上传逻辑与图片类似，但接口可能不同，此处假设使用 /api/file/upload
      // 消息类型为 MESSAGE_TYPE.VIDEO
      const data = { url: filePath };

      const msgInfo = {
        id: 0,
        tmpId: this.generateId(),
        sendId: this.mine.userid,
        content: JSON.stringify(data),
        sendTime: Date.now(),
        selfSend: true,
        type: this.$enums.MESSAGE_TYPE.VIDEO,
        loadStatus: 'loading',
        status: this.$enums.MESSAGE_STATUS.UNSEND,
        receipt: this.isReceipt
      };
      this.fillTargetId(msgInfo, this.chat.targetId);
      this.insertMessage({ msgInfo, chatInfo: this.chat });
      this.moveChatToTop();
      this.scrollToBottom();
      const token = uni.getStorageSync('token');
      uni.uploadFile({
        url: uni.$http.baseUrl + '/api/file/upload',
        filePath: filePath,
        name: 'file',
        header: { Authorization: token },
        success: (res) => {
          const result = JSON.parse(res.data);
          if (result.data) {
            msgInfo.content = JSON.stringify({ url: result.data });
            msgInfo.receipt = this.isReceipt;
            this.sendMessageRequest(msgInfo).then(m => {
              msgInfo.loadStatus = 'ok';
              msgInfo.id = m.id;
              this.insertMessage({ msgInfo, chatInfo: this.chat });
              this.isReceipt = true;
            });
          } else {
            msgInfo.loadStatus = 'fail';
            this.insertMessage({ msgInfo, chatInfo: this.chat });
            uni.showToast({ title: result.msg || '视频上传失败', icon: 'none' });
          }
        },
        fail: () => {
          msgInfo.loadStatus = 'fail';
          this.insertMessage({ msgInfo, chatInfo: this.chat });
          uni.showToast({ title: '视频上传失败', icon: 'none' });
        }
      });
    },

    /**
     * 上传并发送音频
     * @param {Object} audioData - { voice: 文件路径, time: 秒数 }
     */
    uploadAndSendAudio(audioData) {
      const { voice, time } = audioData;
      const data = { url: voice, duration: time };
      const msgInfo = {
        id: 0,
        tmpId: this.generateId(),
        sendId: this.mine.userid,
        content: JSON.stringify(data),
        sendTime: Date.now(),
        selfSend: true,
        type: this.$enums.MESSAGE_TYPE.AUDIO,
        loadStatus: 'loading',
        status: this.$enums.MESSAGE_STATUS.UNSEND,
        receipt: this.isReceipt
      };
      this.fillTargetId(msgInfo, this.chat.targetId);
      this.insertMessage({ msgInfo, chatInfo: this.chat });
      this.moveChatToTop();
      this.scrollToBottom();

      const token = uni.getStorageSync('token');
      uni.uploadFile({
        url: uni.$http.baseUrl + '/api/file/upload',
        filePath: voice,
        name: 'file',
        header: { Authorization: token },
        success: (res) => {
          const result = JSON.parse(res.data);
          if (result.data) {
            msgInfo.content = JSON.stringify({ url: result.data, duration: time });
            msgInfo.receipt = this.isReceipt;
            this.sendMessageRequest(msgInfo).then(m => {
              msgInfo.loadStatus = 'ok';
              msgInfo.id = m.id;
              this.insertMessage({ msgInfo, chatInfo: this.chat });
              this.isReceipt = true;
            });
          } else {
            msgInfo.loadStatus = 'fail';
            this.insertMessage({ msgInfo, chatInfo: this.chat });
            uni.showToast({ title: result.msg || '音频上传失败', icon: 'none' });
          }
        },
        fail: () => {
          msgInfo.loadStatus = 'fail';
          this.insertMessage({ msgInfo, chatInfo: this.chat });
          uni.showToast({ title: '音频上传失败', icon: 'none' });
        }
      });
    },
    sendTextMessage() {
      this.editorCtx.getContents({
        success: e => {
          this.editorCtx.clear();
          if (this.isBanned) {
            this.showBannedTip();
            return;
          }
          let sendText = '';
          e.delta.ops.forEach(op => {
            if (op.insert.image) {
              sendText += `#${op.attributes.alt};`;
            } else {
              sendText += op.insert;
            }
          });
          if (!sendText.trim() && this.atUserIds.length === 0) {
            return uni.showToast({ title: '不能发送空白信息', icon: 'none' });
          }
          // const receiptText = this.isReceipt ? '【回执消息】' : '';
          const atText = this.createAtText();
          const msgInfo = {
            content:  this.html2Escape(sendText) + atText,
            atUserIds: this.atUserIds,
            receipt: this.isReceipt,
            type: 0
          };
          this.atUserIds = [];
          this.isReceipt = true;
          this.fillTargetId(msgInfo, this.chat.targetId);
          this.sendMessageRequest(msgInfo).then(m=> {
            m.selfSend = true;
            this.insertMessage({ msgInfo: m, chatInfo: this.chat });
            this.moveChatToTop();
          }).finally(() => {
            this.scrollToBottom();
          });
        }
      });
    },
    createAtText() {
      let text = '';
      this.atUserIds.forEach(id => {
        if (id === -1) {
          text += ' @全体成员';
        } else {
          const member = this.groupMembers.find(m => m.userId === id);
          if (member) text += ` @${member.showNickName}`;
        }
      });
      return text;
    },
    fillTargetId(msgInfo, targetId) {
      if (this.chat.type === 'GROUP') {
        msgInfo.groupId = targetId;
      } else {
        msgInfo.recvId = targetId;
      }
    },
    scrollToBottom() {
      const size = this.messageSize;
      if (size > 0) {
        this.scrollToMsgIdx(size - 1);
      }
    },
    scrollToMsgIdx(idx) {
      if (idx === this.scrollMsgIdx && idx > 0) {
        this.$nextTick(() => {
          this.scrollMsgIdx = idx - 1;
          this.scrollToMsgIdx(idx);
        });
        return;
      }
      this.$nextTick(() => {
        this.scrollMsgIdx = idx;
      });
    },
    onShowEmoChatTab() {
      this.showRecord = false;
      this.switchChatTabBox('emo');
    },
    onShowToolsChatTab() {
      this.showRecord = false;
      this.switchChatTabBox('tools');
    },
    switchChatTabBox(type) {
      this.chatTabBox = type;
      this.reCalChatMainHeight();
      uni.hideKeyboard();
      // 通知 submit 组件关闭其内部面板
      if (this.$refs.submitRef) {
        this.$refs.submitRef.closeAll();
      }
      // if (type !== 'tools' && this.$refs.fileUpload) {
      //   this.$refs.fileUpload.hide();
      // }
    },
    selectEmoji(emoText) {
      const path = this.$emo.textToPath(emoText);
      this.isReadOnly = true;
      this.isEmpty = false;
      this.$nextTick(() => {
        this.editorCtx.insertImage({
          src: path,
          alt: emoText,
          extClass: 'emoji-small',
          nowrap: true,
          complete: () => {
            this.isReadOnly = false;
            this.editorCtx.blur();
          }
        });
      });
    },
    onUploadImageBefore(file) {
      if (this.isBanned) {
        this.showBannedTip();
        return false;
      }
      const data = { originUrl: file.path, thumbUrl: file.path };
      const msgInfo = {
        id: 0,
        tmpId: this.generateId(),
        fileId: file.uid,
        sendId: this.info.userid,
        content: JSON.stringify(data),
        sendTime: Date.now(),
        selfSend: true,
        type: this.$enums.MESSAGE_TYPE.IMAGE,
        readedCount: 0,
        loadStatus: 'loading',
        status: this.$enums.MESSAGE_STATUS.UNSEND
      };
      this.fillTargetId(msgInfo, this.chat.targetId);
      this.insertMessage({ msgInfo, chatInfo: this.chat });
      this.moveChatToTop();
      file.msgInfo = msgInfo;
      file.chat = this.chat;
      this.scrollToBottom();
      return true;
    },
    onUploadImageSuccess(file, res) {
      const msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
      msgInfo.content = JSON.stringify(res.data);
      msgInfo.receipt = this.isReceipt;
      this.sendMessageRequest(msgInfo).then( res=> {
        var m =res.data.data
        msgInfo.loadStatus = 'ok';
        msgInfo.id = m.id;
        this.isReceipt = true;
        this.insertMessage({ msgInfo, chatInfo: file.chat });
      });
    },
    onUploadImageFail(file) {
      const msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
      msgInfo.loadStatus = 'fail';
      this.insertMessage({ msgInfo, chatInfo: file.chat });
    },
    onUploadFileBefore(file) {
      if (this.isBanned) {
        this.showBannedTip();
        return false;
      }
      const data = { name: file.name, size: file.size, url: file.path };
      const msgInfo = {
        id: 0,
        tmpId: this.generateId(),
        sendId: this.info.userid,
        content: JSON.stringify(data),
        sendTime: Date.now(),
        selfSend: true,
        type: this.$enums.MESSAGE_TYPE.FILE,
        readedCount: 0,
        loadStatus: 'loading',
        status: this.$enums.MESSAGE_STATUS.UNSEND
      };
      this.fillTargetId(msgInfo, this.chat.targetId);
      this.insertMessage({ msgInfo, chatInfo: this.chat });
      this.moveChatToTop();
      file.msgInfo = msgInfo;
      file.chat = this.chat;
      this.scrollToBottom();
      return true;
    },
    onUploadFileSuccess(file, res) {
      const data = { name: file.name, size: file.size, url: res.data };
      const msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
      msgInfo.content = JSON.stringify(data);
      msgInfo.receipt = this.isReceipt;
      this.sendMessageRequest(msgInfo).then(res => {
        var m=res.data.data
        msgInfo.loadStatus = 'ok';
        msgInfo.id = m.id;
        this.isReceipt = true;
        this.insertMessage({ msgInfo, chatInfo: file.chat });
      });
    },
    onUploadFileFail(file) {
      const msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
      msgInfo.loadStatus = 'fail';
      this.insertMessage({ msgInfo, chatInfo: file.chat });
    },
    onDeleteMessage(msgInfo) {
      uni.showModal({
        title: '删除消息',
        content: '确认删除消息?',
        success: res => {
          if (!res.cancel) {
            this.deleteMessage({ msgInfo, chatInfo: this.chat });
            uni.showToast({ title: '删除成功', icon: 'none' });
          }
        }
      });
    },
    async onRecallMessage(msgInfo) {
      uni.showModal({
        title: '撤回消息',
        content: '确认撤回消息?',
        success: res => {
          if (!res.cancel) {
            // const url = `/message/${this.chat.type.toLowerCase()}/recall/${msgInfo.id}`;
            //  uni.$http.delete(url).then(m => {
            //   m.selfSend = true;
            //   this.recallMessage({ msgInfo: m, chatInfo: this.chat });
            // });
          }
        }
      });
    },
    onCopyMessage(msgInfo) {
      uni.setClipboardData({
        data: msgInfo.content,
        success: () => uni.showToast({ title: '复制成功' }),
        fail: () => uni.showToast({ title: '复制失败', icon: 'none' })
      });
    },
    onDownloadFile(msgInfo) {
      const url = JSON.parse(msgInfo.content).url;
      uni.downloadFile({
        url,
        success(res) {
          if (res.statusCode === 200) {
            const filePath = encodeURI(res.tempFilePath);
            uni.openDocument({ filePath, showMenu: true });
          }
        },
        fail() {
          uni.showToast({ title: '文件下载失败', icon: 'none' });
        }
      });
    },
    onClickToBottom() {
      this.scrollToBottom();
      setTimeout(() => {
        this.isInBottom = true;
        this.newMessageSize = 0;
      }, 100);
    },
    onScrollToTop() {
      if (this.showMinIdx > 0) {
        // #ifndef H5
        this.scrollToMsgIdx(this.showMinIdx);
        // #endif
        this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
      }
      this.isInBottom = false;
    },
    onScrollToBottom() {
      this.isInBottom = true;
      this.newMessageSize = 0;
    },
    onShowMore() {
      if (this.chat.type === 'GROUP') {
        uni.navigateTo({ url: `/pages/group/group-info?id=${this.group.id}` });
      } else {
        uni.navigateTo({ url: `/pages/common/user-info?id=${this.userInfo.id}` });
      }
    },
    onTextInput(e) {
      this.isEmpty = e.detail.html === '<p><br></p>';
    },
    onEditorReady() {
      this.$nextTick(() => {
        const query = uni.createSelectorQuery().in(this);
        query.select('#editor').context(res => {
          this.editorCtx = res.context;
        }).exec();
      });
    },
    onEditorFocus() {
      this.isFocus = true;
      this.scrollToBottom();
      this.switchChatTabBox('none');
    },
    onEditorBlur() {
      this.isFocus = false;
    },
    onAudioStateChange(state, msgInfo) {
      const playingAudio = this.$refs['message' + msgInfo.id]?.[0];
      if (state === 'PLAYING' && playingAudio !== this.playingAudio) {
        this.playingAudio?.stopPlayAudio();
        this.playingAudio = playingAudio;
      }
    },
    //获取最大已读id
    async loadReaded(fid) {
      await uni.$http.get( `/api/message/private/maxReadedId?friendId=${fid}`).then(res => {
        //获取最大已读id
        this.readedMessage({ friendId: fid, maxId: res.data.data });
      });
    },
    async toReadedMessage() {
      if (this.unreadCount === 0) return;
      const url = this.chat.type === 'GROUP'
          ? `/api/message/group/readed?groupId=${this.chat.targetId}`
          : `/api/message/private/readed?friendId=${this.chat.targetId}`;
      await uni.$http.put(url).then(() => {
        this.resetUnreadCount(this.chat);
      });
    },
    async loadGroup(groupId) {
      await uni.$http.get(`/api/group/find/${groupId}`).then(res => {
        var group =res.data.data
        this.group = group;
        this.updateChatFromGroup(group);
        this.updateGroup(group);
      });
      await uni.$http.get(`/api/group/members/${groupId}`).then(members => {
        this.groupMembers = members.data.data;
      });
    },
    updateFriendInfo() {
      if (this.friend) {
        const friend = JSON.parse(JSON.stringify(this.friend));
        friend.headImage = this.userInfo.headImageThumb;
        friend.nickName = this.userInfo.nickName;
        friend.showNickName = friend.remarkNickName || friend.nickName;
        this.updateFriend(friend);
        this.updateChatFromFriend(friend);
      } else {
        this.updateChatFromUser(this.userInfo);
      }
    },
    async loadFriend(friendId) {
      await uni.$http.get( `/api/friend/find/${friendId}`).then(userInfo => {
        this.userInfo = userInfo.data.data;
        this.updateFriendInfo();
      });
    },
    rpxTopx(rpx) {
      const info = uni.getSystemInfoSync();
      return Math.floor(info.windowWidth * rpx / 750);
    },
    html2Escape(str) {
      return str.replace(/[<>&"]/g, c => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;' }[c]));
    },
    sendMessageRequest(msgInfo) {
      return new Promise((resolve, reject) => {
        this.reqQueue.push({ msgInfo, resolve, reject });
        this.processReqQueue();
      });
    },
    async processReqQueue() {
      if (this.reqQueue.length && !this.isSending) {
        this.isSending = true;
        const reqData = this.reqQueue.shift();
        await uni.$http.post( this.messageAction,reqData.msgInfo)
            .then(res => reqData.resolve(res.data.data))
            .catch(e => reqData.reject(e))
            .finally(() => {
              this.isSending = false;
              this.processReqQueue();
            });
      }
    },
    reCalChatMainHeight() {
      setTimeout(() => {
        let h = this.windowHeight;
        // 减去标题栏高度
        h -= 0;
        // 减去键盘高度
        // if (this.isShowKeyBoard || this.chatTabBox !== 'none') {
        //   h -= this.keyboardHeight;
        //   this.scrollToBottom();
        // }
        // #ifndef H5
        // h5需要减去状态栏高度
        h -= uni.getSystemInfoSync().statusBarHeight;
        // #endif
        this.chatMainHeight = h;
        if (this.isShowKeyBoard || this.chatTabBox !== 'none') {
          this.scrollToBottom();
        }
        // #ifdef H5
        if (uni.getSystemInfoSync().platform === 'ios') {
          [50, 100, 500].forEach(delay => {
            setTimeout(() => {
              uni.pageScrollTo({ scrollTop: 0, duration: 10 });
            }, delay);
          });
        }
        // #endif
      }, 30);
    },
    listenKeyBoard() {
      // #ifdef H5
      if (navigator.platform === 'Win32') {
        console.log('navigator.platform:', navigator.platform);
        return;
      }
      if (uni.getSystemInfoSync().platform === 'ios') {
        window.addEventListener('focusin', this.focusInListener);
        window.addEventListener('focusout', this.focusOutListener);
        if (window.visualViewport) {
          window.visualViewport.addEventListener('resize', this.resizeListener);
        }
      } else {
        window.addEventListener('resize', this.resizeListener);
      }
      // #endif
      // #ifndef H5
      uni.onKeyboardHeightChange(this.keyBoardListener);
      // #endif
    },
    unListenKeyboard() {
      // #ifdef H5
      window.removeEventListener('resize', this.resizeListener);
      window.removeEventListener('focusin', this.focusInListener);
      window.removeEventListener('focusout', this.focusOutListener);
      // #endif
      // #ifndef H5
      uni.offKeyboardHeightChange(this.keyBoardListener);
      // #endif
    },
    keyBoardListener(res) {
      this.isShowKeyBoard = res.height > 0;
      if (this.isShowKeyBoard) this.keyboardHeight = res.height;
      this.reCalChatMainHeight();
    },
    resizeListener() {
      let height = this.initHeight - window.innerHeight;
      if (window.visualViewport && uni.getSystemInfoSync().platform === 'ios') {
        height = this.initHeight - window.visualViewport.height;
      }
      console.log('resizeListener:', window.visualViewport?.height);
      this.isShowKeyBoard = height > 150;
      if (this.isShowKeyBoard) this.keyboardHeight = height;
      this.reCalChatMainHeight();
    },
    focusInListener() {
      this.isShowKeyBoard = true;
      this.reCalChatMainHeight();
    },
    focusOutListener() {
      this.isShowKeyBoard = false;
      this.reCalChatMainHeight();
    },
    showBannedTip() {
      const msgInfo = {
        tmpId: this.generateId(),
        sendId: this.mine.userid,
        sendTime: Date.now(),
        type: this.$enums.MESSAGE_TYPE.TIP_TEXT
      };
      if (this.chat.type === 'PRIVATE') {
        msgInfo.recvId = this.mine.userid;
        msgInfo.content = `该用户已被管理员封禁，原因：${this.userInfo.reason}`;
      } else {
        msgInfo.groupId = this.group.id;
        msgInfo.content = `本群聊已被管理员封禁，原因：${this.group.reason}`;
      }
      this.insertMessage({ msgInfo, chatInfo: this.chat });
    },
    generateId() {
      return String(Date.now()) + String(Math.floor(Math.random() * 1000));
    }
  }
};
</script>

<style lang="scss">
.chat-box {
  $icon-color: rgba(0, 0, 0, 0.88);
  position: relative;
  background-color: rgba(244, 244, 244, 1);


  .chat-main-box {
    position: fixed;
    width: 100%;
    display: flex;
    flex-direction: column;
    z-index: 2;

    .chat-message {
      flex: 1;
      padding: 0;
      overflow: hidden;
      position: relative;
      background-color: white;

      .scroll-box {
        height: 100%;
      }

      .scroll-to-bottom {
        position: absolute;
        //
        right: 30rpx;
        //bottom: 30rpx;
        font-size: $im-font-size;
        color: $im-color-primary;
        font-weight: 600;
        background: white;
        padding: 10rpx 30rpx;
        border-radius: 25rpx;
        box-shadow: $im-box-shadow-dark;
      }
    }

  }

}
</style>
