vue 聊天页面

需求:店铺需要跟用户对话
我直接上代码

<template>
  <div class="message-detail message-detail-list">
    <section class="chat-section">
      <a-card class="chat-card">
        <div class="chat-title">
          <div>
            <p class="te-im">
              {{ queryParams.userName }}
              <img src="@/assets/userImg/info-blue.svg" />
            p>
            <p>{{ queryParams.recruitTime }} {{ queryParams.title }}p>
          div>
          <a-button @click="toUserDetail(queryParams)">{{ $t('evaluation.detail') }}a-button>
        div>
        <a-card class="chat-section chat-card1">
          
          <div id="chat-box1" ref="msg-box" class="box">
            <div v-for="(item, index) in listData" :key="index">
              <div class="chart-timer">{{ item.createTime }}div>
              //recruit_app判断用户发过来的信息,展示在左侧
              <div v-show="item.businessType === 'recruit_app'" class="item chart-left">
                <div v-if="!imgUrl" class="default-avatar">
                  <svg-icon icon-class="store" />
                div>
                <img class="header-img" :src="imgUrl" />
                <span class="message">{{ item.message }}span>
              div>
              //recruit_enterprise判断客服(自己发过去)发过来的信息,展示在右侧侧
              <div v-show="item.businessType == 'recruit_enterprise'" class="item chart-right">
                <div v-if="!user.avatar" class="default-avatar">
                  <svg-icon icon-class="store" />
                div>
                <img v-else class="header-img" :src="user.avatar" />
                <span class="message">{{ item.message }}span>
              div>
            div>
          div>
        a-card>
        <div class="input-box">
          <a-input
            ref="sendMsg"
            v-model="contentText"
            type="text"
            :placeholder="$t('evaluation.pleaseFillIn')"
            @keyup.enter.native="sendText"
          />
          <a-button v-if="contentText" class="btn" @click="sendText">
            <img src="@/assets/userImg/send-wirte.svg" />
            {{ $t('evaluation.send') }}
          a-button>
          <a-button v-else class="btn1">
            <img src="@/assets/userImg/send-wirte.svg" />
            {{ $t('evaluation.send') }}
          a-button>
        div>
      a-card>
    section>
  div>
template>
import { mapState } from 'vuex';
import { getToken } from '@/utils/auth';
import { listHistory } from '@/api/manage/message';
export default {
  name: 'MessageDetail',
  data() {
    return {
      queryParams: {},
      // table/搜索框
      tableLoading: false,
      // 分页数据(默认第一页):
      pageSizeOptions: ['10', '20', '30', '40', '50'],
      current: 1,
      pageSize: 10,
      total: 0,
      socket: '',
      ws: null,
      contentText: '',
      // 历史分页参数
      historyParams: {
        pageSize: 10, // 加载多少页到多少页的数据
        pageNum: 1, // 加载第几页的数据
      },
      oppositeData: {},
      weData: {},
      listData: [
        // { 'appUserId': 1, 'appUserName': '国航', 'message': '服务器消息', 'recruitId': 22, 'type': 'server' },
        // { 'appUserId': 1, 'appUserName': '国航', 'message': '服务器消1息31232', 'recruitId': 22 },
      ], // 聊天记录的数组
      isShow: false,
      isShow1: false,
      chatList: [],
      isFirst: 1,
      lockReconnect: true, // 避免重复连接
      imgUrl: null,
      page: 0,
    };
  },
  computed: {
    ...mapState(['settings', 'user', 'mode']),
  },
  created() {
    this.queryParams = JSON.parse(localStorage.getItem('queryParamsMessage'));
    this.imgUrl = this.queryParams.imgUrl;
    this.getListHistory();
    // 初始化
    this.$nextTick(() => {
      this.initWebSocket();
    });
  },
  destroyed() {
    // 销毁监听
    window.removeEventListener('mousewheel', this.onScroll, false);
  },
  mounted() {
    this.$nextTick(() => {
      window.addEventListener('mousewheel', this.onScroll, true);
    });
  },
  methods: {
    getListHistory() {
      const params = {
        id: this.queryParams.id,
        userId: this.queryParams.userId,
        isFirst: this.isFirst,
        recruitId: this.queryParams.recruitId,
        page: this.page,
        // ...this.historyParams,
      };
      listHistory(params).then((res) => {
        if (res.code === 200) {
          this.$store.dispatch('SetUnread');
          if (res.data.length < 1) {
            if (this.page === -1) {
              this.isShow = true;
            } else {
              this.isShow1 = true;
            }
          } else {
            this.queryParams.id = res.data[0].id;
          }
          this.listData = [...this.listData, ...res.data];
          this.listData = this.listData.sort(function (a, b) {
            return a.createTime > b.createTime ? 1 : -1;
          });
        }
      });
    },
    // 获取滚动条当前的位置
    getScrollTop() {
      var scrollTop = 0;
      // const dom = document.getElementById('chat-box1');
      if (document.documentElement && document.documentElement.scrollTop) {
        scrollTop = document.documentElement.scrollTop;
      } else if (document.body) {
        scrollTop = document.body.scrollTop;
      }
      return scrollTop;
    },
    // 获取当前可视范围的高度
    getClientHeight() {
      var clientHeight = 0;
      if (document.body.clientHeight && document.documentElement.clientHeight) {
        clientHeight = Math.min(document.body.clientHeight, document.documentElement.clientHeight);
      } else {
        clientHeight = Math.max(document.body.clientHeight, document.documentElement.clientHeight);
      }
      return clientHeight;
    },
    // 获取文档完整的高度
    getScrollHeight() {
      return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
    },
    // 滚动事件触发下拉加载
    onScroll(e) {
      const direction = e.deltaY > 0 ? 'down' : 'up'; // 该语句可以用来判断滚轮是向上滑动还是向下
      if (direction === 'up') {
        this.page = -1;
        // console.log('isShow:' + this.isShow);
        if (!this.isShow) {
          this.$nextTick(() => {
            this.getListHistory();
          });
        }
      } else {
        this.page = 1;
        // console.log('isShow1:' + this.isShow1);
        if (!this.isShow1) {
          this.$nextTick(() => {
            this.getListHistory();
          });
        }
      }
      if (this.getScrollHeight() - this.getClientHeight() - this.getScrollTop() <= 0) {
        // if (this.status === 1) {
        //   this.status = 0;
        // 页码,分页用,默认第一页
        // this.deliverParams.page += 1;
        // 调用请求函数
        //   alert('触发!!!');
        // }
      }
    },
    /** pageSize 变化的回调 */
    onShowSizeChange(current, pageSize) {
      this.pageSize = pageSize;
      this.current = current;
      this.getList();
    },
    /** 页码改变的回调 */
    currentPageChange(current, pageSize) {
      this.pageSize = pageSize;
      this.current = current;
      this.getList();
    },
    toDetailMessage() {
      this.$router.push({ path: '/message-detail/detail' });
    },
    returnClick() {
      this.$router.go(-1);
    },
    toScan() {
      this.$router.push({ path: '/attendance' });
    },
    toAddUser() {
      this.$router.push({ path: '/recruit-temp/index' });
    },
    toUserDetail(record) {
      this.$router.push({ path: '/recruit/detail', query: { id: record.recruitId }});
    },
    // 滚动条到底部
    scrollBottom() {
      const el = this.$refs['msg-box'];
      el.scrollTop = el.scrollHeight;
    },
    // 发送聊天信息
    sendText() {
      const _this = this;
      _this.$refs['sendMsg'].focus();
      if (!_this.contentText) {
        return;
      }
      const param = this.queryParams;
      this.chatList = {
        appUserId: param.userId,
        appUserName: param.userName,
        recruitId: param.recruitId,
        message: _this.contentText,
      };
      if (_this.ws.readyState === 1) {
        _this.ws.send(JSON.stringify(this.chatList)); // 调用WebSocket send()发送信息的方法
        this.listData.push({ ...this.chatList, businessType: 'recruit_enterprise' });
      }
      _this.contentText = '';
      setTimeout(() => {
        _this.scrollBottom();
      }, 500);
    },
    destroyed() {
      // 销毁监听
      // this.over();
      this.ws.close();
    },
    // 进入页面创建websocket连接
    initWebSocket() {
      const _this = this;
      const NODE_ENV = process.env.NODE_ENV;
      let ws = null;
      // 判断页面有没有存在websocket连接
      if (window.WebSocket) {
        if (NODE_ENV === 'development') {
          //  此处的 :8181 端口号 要与后端配置的一致
          ws = new WebSocket('ws://13.230.192.185:81/msg;' + getToken());
        } else if (NODE_ENV === 'production') {
          ws = new WebSocket('ws://13.230.192.185:81/msg;' + getToken());
        } else {
          ws = new WebSocket('ws://13.230.192.185:81/msg;' + getToken());
        }
        _this.ws = ws;
        ws.onopen = function (e) {
          console.log('服务器连接成功');
        };
        ws.onclose = function (e) {
          console.log('服务器连接关闭');
        };
        ws.onerror = function (e) {
          if (_this.lockReconnect) {
            setTimeout(() => {
              _this.initWebSocket();
              _this.$message.error(_this.$t('evaluation.again'));
            }, 5000);
          }
          console.log('服务器连接出错');
        };
        ws.onmessage = function (e) {
          // 接收服务器返回的数据
          if (e.data.indexOf('{') !== -1) {
            // 接收对方数据
            const data = JSON.parse(e.data);
            if (_this.queryParams.recruitId === data.recruitId) {
              _this.listData.push({ ...data, businessType: 'recruit_app' });
            }
          }
        };
        _this.$router.afterEach(function (e) {
          if (e.name === 'DetailMessage') {
            _this.lockReconnect = true;
          } else {
            _this.lockReconnect = false;
          }
        });
      }
    },
  },
<style lang="scss" scoped>
.chat-load {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #409eff;
  margin: -11px 0 20px 0;
}
.message-detail {
  padding: 10px 48px;
  max-width: 1200px;

  .chat-card1 {
    background: #f5f5f5ff;
    border-radius: 10px;
    height: 600px;
    overflow: auto;
  }
  .chat-section {
    margin-top: 40px;
    padding: 20px 20px 60px 20px;

    .chat-card {
      border-radius: 10px;
      padding: 20px 20px 60px 20px;
      .chat-title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        .te-im {
          display: flex;
          align-items: center;
          img {
            width: 16px;
            margin-left: 10px;
          }
        }
      }
    }

    /*
聊天item采用flex布局
*/
    .item {
      display: flex;
      margin-bottom: 20px;
    }

    .chart-left {
      flex-direction: row;
      .message {
        border-radius: 0 10px 10px 10px;
        background: #fff;
        align-items: center;
        color: #333333ff;
        margin-left: 10px;
      }
    }

    .chart-right {
      flex-direction: row-reverse;
      .message {
        border-radius: 10px 0 10px 10px;
        background: #26d4e0ff;
        color: #fff;
        margin-right: 10px;
      }
    }

    .header-img {
      width: 42px;
      height: 42px;
      border-radius: 100px;
    }

    .message {
      display: flex;
      min-height: 25px;
      padding: 9px 10px;
      align-items: center;
      word-break: break-all;
      max-width: 41%;
    }

    .input-box {
      position: absolute;
      bottom: 0px;
      left: 0;
      right: 0;
      display: flex;
      padding: 15px 40px;
      box-sizing: border-box;
    }

    .input-box input {
      flex: 1;
      border-radius: 3px;
      border: 1px #cecece solid;
      padding: 20px;
      outline: none;
    }
    .btn {
      background: #26d4e0ff;
    }
    .btn1 {
      background: #ccccccff;
    }
    .input-box button {
      width: 80px;
      border-radius: 3px;
      border: 1px #fffa solid;
      color: #ffffff;
      margin: 0px 6px;
      outline: none;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 20px;
      img {
        width: 14.71px;
        margin-right: 5px;
      }
    }
    button:active {
    }

    .chart-timer {
      text-align: center;
      color: #616161;
      font-size: 13px;
    }
  }
}
.chat-box {
  margin: 0 auto;
  background: #fafafa;
  position: absolute;
  height: 100%;
  width: 100%;
  max-width: 700px;
  header {
    position: fixed;
    width: 100%;
    height: 3rem;
    background: #409eff;
    max-width: 700px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: bold;
    color: white;
    font-size: 1rem;
  }
  .msg-box {
    position: absolute;
    height: calc(100% - 6.5rem);
    width: 100%;
    margin-top: 3rem;
    overflow-y: scroll;
    .msg {
      width: 95%;
      min-height: 2.5rem;
      margin: 1rem 0.5rem;
      position: relative;
      display: flex;
      justify-content: flex-start !important;
      .user-head {
        min-width: 2.5rem;
        width: 20%;
        width: 2.5rem;
        height: 2.5rem;
        border-radius: 50%;
        background: #f1f1f1;
        display: flex;
        justify-content: center;
        align-items: center;
        .head {
          width: 1.2rem;
          height: 1.2rem;
        }
        // position: absolute;
      }
      .user-msg {
        width: 80%;
        // position: absolute;
        word-break: break-all;
        position: relative;
        z-index: 5;
        span {
          display: inline-block;
          padding: 0.5rem 0.7rem;
          border-radius: 0.5rem;
          margin-top: 0.2rem;
          font-size: 0.88rem;
        }
        .left {
          background: white;
          animation: toLeft 0.5s ease both 1;
        }
        .right {
          background: #53a8ff;
          color: white;
          animation: toright 0.5s ease both 1;
        }
        @keyframes toLeft {
          0% {
            opacity: 0;
            transform: translateX(-10px);
          }
          100% {
            opacity: 1;
            transform: translateX(0px);
          }
        }
        @keyframes toright {
          0% {
            opacity: 0;
            transform: translateX(10px);
          }
          100% {
            opacity: 1;
            transform: translateX(0px);
          }
        }
      }
    }
  }
  .input-box {
    padding: 0 0.5rem;
    position: absolute;
    bottom: 0;
    width: 100%;
    height: 3.5rem;
    background: #fafafa;
    box-shadow: 0 0 5px #ccc;
    display: flex;
    justify-content: space-between;
    align-items: center;
    input {
      height: 2.3rem;
      display: inline-block;
      width: 100%;
      padding: 0.5rem;
      border: none;
      border-radius: 0.2rem;
      font-size: 0.88rem;
    }
    .btn {
      height: 2.3rem;
      min-width: 4rem;
      background: #e0e0e0;
      padding: 0.5rem;
      font-size: 0.88rem;
      color: white;
      text-align: center;
      border-radius: 0.2rem;
      margin-left: 0.5rem;
      transition: 0.5s;
    }
    .btn-active {
      background: #409eff;
    }
  }
}
.default-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  background-color: #fff;
  color: #999999;
  border-radius: 50%;
  font-size: 50px;
  padding: 6px;
}
style>

你可能感兴趣的:(前端VUE,vue.js,前端,javascript)