• 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">
              <!-- <span class="chat-load">加载更多信息</span> -->
              <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    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;
              }
            });
          }
        },
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    <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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
  • 相关阅读:
    Android提供了多种方式来打开特定文件夹中的视频
    深入探索C语言自定义类型:打造你的编程世界
    ES6 入门教程 29 ArrayBuffer 29.2 TypedArray 视图
    Redis查询,设置超时时间
    C语言--文件操作详解(1)文件操作的基本概念及文件操作函数用法举例
    OpenHarmony实战开发-组件复用实践。
    锂电池行业走向数字化,B2B电商系统精益企业库存管控,实现仓储数字化管理
    python 3 国内镜像 (阿里、清华)
    iOS图文混排 头部有图片等相关内容的trick实现方式
    js制作动态表单
  • 原文地址:https://blog.csdn.net/weixin_45869649/article/details/125411031