vue实现可拖动智能聊天对话框

项目中的一个需求,可以智能咨询问题(数据库写好的问题和答案,根据你输入问题的关键字调取接口,返回数据)
如图所示:
在这里插入图片描述
把鼠标放在红色头部区域可以拖动,不会超出当前可视区域。点击方块可以放大占满全屏,再次点击变回正常大小。点击x隐藏对话框。
把鼠标放在对话框的边上可以上下左右拖拽放大。拖拽和拖拽边框放大我是参考的这个网址里的,写的很好,有需要的话可以去看看(https://www.jb51.net/article/73157.htm)。

废话不多说,上代码。

<template>
  <div>
    <img src="../../assets/th.png" alt id="robot" @click="toTalk" style="cursor:pointer" />
    <div id="box" :style=" flag ? 'display:none':'display:block'">
      <!-- 主体 -->
      <div class="container">
        <div id="header">
          <span style="display:inline-block">名字</span>
          <p>
            <span @click="changeSize" style="font-size:22px;cursor:pointer;color:#fff;display:inline-block">🔳</span>
           	<!--这个方块是我懒得引入icon图标随便复制的,不要学我省事,因为有的浏览器可能不显示-->
            <span
              @click="yc"
              style="cursor: pointer;font-size:22px;display:inline-block;margin-left:10px;color:#fff;"
            >✖</span>
          </p>
        </div>
        <!-- 聊天内容显示区 -->
        <div class="cBox">
          <div class="contents" ref="chattingContent" id="chattingContents">
            <div v-for="(item,index) in msgs" :key="index">
              <div class="userQuestion self" v-if="item.self">
                <p class="question">{{item.content}}</p>
                <img :src="item.avatarUrl" alt class="uImg" />
              </div>
              <div class="robotAnswer other clearfix" v-else>
                <img :src="item.avatarUrl" alt class="rImg" />
                <div class="answerContent">
                  <p class="q">{{item.q}}</p>
                  <p class="a">{{item.a}}</p>
                </div>
              </div>
            </div>
          </div>
          <!-- 用户问题输入区 -->
          <div class="userInput">
            <div class="tBox">
              <textarea
                class="ipt"
                placeholder="开始聊天"
                @keyup.enter="sendMsgs"
                v-model.trim="inputContent"
              ></textarea>
            </div>
            <vp-button type="primary" @click="sendMsgs">发 送</vp-button>
          </div>
        </div>
      </div>
      <!-- 实现拖拽对话框和拖拽边放大 -->
      <div
        id="resizeL"
        style="position:absolute;overflow:hidden;opacity:0;filter:alpha(opacity=0);top:0;width:5px;height:100%;cursor:w-resize;"
      ></div>
      <div
        id="resizeT"
        style="position:absolute;overflow:hidden;opacity:0;filter:alpha(opacity=0);width:100%;height:5px;cursor:n-resize;top:0;"
      ></div>
      <div
        id="resizeR"
        style="position:absolute;overflow:hidden;opacity:0;filter:alpha(opacity=0);top:0;width:5px;height:100%;cursor:w-resize;right:0;"
      ></div>
      <div
        id="resizeB"
        style="position:absolute;overflow:hidden;opacity:0;filter:alpha(opacity=0);width:100%;height:5px;cursor:n-resize;bottom:0;"
      ></div>
    </div>
  </div>
</template>
<script>
import { getRobotAnswer } from '@/api/smartManage';
import userImg from '../../assets/user-avatar.jpeg';
import robotImg from '../../assets/robot-avatar.jpg';
export default {
  data() {
    return {
      msgs: [], //用来存放对话
      inputContent: '',
      oContent: {},
      flag: true,
      flag2: true,
    //我定义flagflag2的初衷是为了当我点击方块放大对话框后,点击x隐藏对话框,当我再次呼出对话框时,想让对话框的大小为正常大小
    //而不是放大后的大小。如果你没有点击方块放大的需求,这部分可以不用写。
    };
  },
  mounted() {
    this.oContent = document.getElementById('chattingContents');
    setTimeout(() => {
      this.$refs.chattingContent.scrollTop = this.$refs.chattingContent.scrollHeight;
    }, 0);
    function dragable(id) {
      var d = document,
        o = d.getElementById(id),
        x,
        y,
        p = 'ondrag';
      o.ondragstart = function(e) {
        e = e || event;
        x = e.clientX - o.offsetLeft;
        y = e.clientY - o.offsetTop;
        d.ondragend = function(e) {
          e = e || event;
          o.style.left = e.clientX - x + 'px';
          o.style.top = e.clientY - y + 'px';
        };
      };
    }
    function drag(oDrag, handle) {
      var disX = 0;
      var disY = 0;
      handle = handle || oDrag;
      handle.style.cursor = 'move';
      handle.onmousedown = function(e) {
        var event = event || window.event;
        disX = event.clientX - oDrag.offsetLeft;
        disY = event.clientY - oDrag.offsetTop;
        document.onmousemove = function(e) {
          var event = event || window.event;
          var iL = event.clientX - disX;
          var iT = event.clientY - disY;
          var maxL = document.documentElement.clientWidth - oDrag.offsetWidth;
          var maxT = document.documentElement.clientHeight - oDrag.offsetHeight;
          iL <= 0 && (iL = 0);
          iT <= 0 && (iT = 0);
          iL >= maxL && (iL = maxL);
          iT >= maxT && (iT = maxT);
          oDrag.style.left = iL + 'px';
          oDrag.style.top = iT + 'px';
          return false;
        };
        document.onmouseup = function() {
          document.onmousemove = null;
          this.releaseCapture && this.releaseCapture();
        };
        this.setCapture && this.setCapture();
        return false;
      };
    }
    var dragMinWidth = 500;
    var dragMinHeight = 450;
    function resize(oParent, handle, isLeft, isTop, lockX, lockY) {
      handle.onmousedown = function(e) {
        var event = event || window.event;
        var disX = event.clientX - handle.offsetLeft;
        var disY = event.clientY - handle.offsetTop;
        var iParentTop = oParent.offsetTop;
        var iParentLeft = oParent.offsetLeft;
        var iParentWidth = oParent.offsetWidth;
        var iParentHeight = oParent.offsetHeight;
        document.onmousemove = function(e) {
          var event = event || window.event;
          var iL = event.clientX - disX;
          var iT = event.clientY - disY;
          var maxW =
            document.documentElement.clientWidth - oParent.offsetLeft - 2;
          var maxH =
            document.documentElement.clientHeight - oParent.offsetTop - 2;
          var iW = isLeft ? iParentWidth - iL : handle.offsetWidth + iL;
          var iH = isTop ? iParentHeight - iT : handle.offsetHeight + iT;
          isLeft && (oParent.style.left = iParentLeft + iL + 'px');
          isTop && (oParent.style.top = iParentTop + iT + 'px');
          iW < dragMinWidth && (iW = dragMinWidth);
          iW > maxW && (iW = maxW);
          lockX || (oParent.style.width = iW + 'px');
          iH < dragMinHeight && (iH = dragMinHeight);
          iH > maxH && (iH = maxH);
          lockY || (oParent.style.height = iH + 'px');
          if ((isLeft && iW == dragMinWidth) || (isTop && iH == dragMinHeight))
            document.onmousemove = null;
          return false;
        };
        document.onmouseup = function() {
          document.onmousemove = null;
          document.onmouseup = null;
        };
        return false;
      };
    }
    var oDrag = document.getElementById('box');
    var oL = document.getElementById('resizeL');
    var oT = document.getElementById('resizeT');
    var oR = document.getElementById('resizeR');
    var oB = document.getElementById('resizeB');
    var oTitle = document.getElementById('header');
    drag(oDrag, oTitle);
    resize(oDrag, oL, true, false, false, true);
    resize(oDrag, oT, false, true, true, false);
    resize(oDrag, oR, false, false, false, true);
    resize(oDrag, oB, false, false, true, false);
    oDrag.style.left =
      (document.documentElement.clientWidth - oDrag.offsetWidth) / 2 + 'px';
    oDrag.style.top =
      (document.documentElement.clientHeight - oDrag.offsetHeight) / 2 + 'px';
    dragable('robot');
  },
  methods: {
    //点x隐藏对话框
    yc() {
      let box = document.getElementById('box');
      box.style.display = 'none';
      this.flag = !this.flag;
    },
    //点击你放好的图标或者块出现对话框
    toTalk() {
      if (!this.flag2) {
        this.flag = !this.flag;
        let box = document.getElementById('box');
        box.style.width = '500px';
        box.style.height = '450px';
        box.style.left = '33%';
        box.style.top = '20%';
        this.flag2 = !this.flag2;
      } else {
        this.flag = !this.flag;
        let box = document.getElementById('box');
        box.style.width = '500px';
        box.style.height = '450px';
        box.style.left = '33%';
        box.style.top = '20%';
      }
    },
    //点击放大对话框
    changeSize() {
      if (this.flag2) {
        let box = document.getElementById('box');
        box.style.width = '100%';
        box.style.height = '100%';
        box.style.left = '0';
        box.style.top = '0';
        this.flag2 = !this.flag2;
      } else {
        let box = document.getElementById('box');
        box.style.width = '500px';
        box.style.height = '450px';
        box.style.left = '33%';
        box.style.top = '20%';
        this.flag2 = !this.flag2;
      }
    },
    //发送消息
    sendMsgs() {
      this.oContent.scrollTop = this.oContent.scrollHeight;
      if (this.inputContent === '') {
        return;
      }
      this.msgs.push({
        content: this.inputContent,
        avatarUrl: userImg,
        self: true,
      });
      //self的作用是为了判断是用户还是机器人,用户的话渲染在对话框的右边,机器人的回答渲染在对话框的左边
      this.getResult();
      setTimeout(() => {
        this.$refs.chattingContent.scrollTop = this.$refs.chattingContent.scrollHeight;
      }, 0);
      this.inputContent = '';
    },
    //智能机器人回复
    getResult() {
      let that = this;
      var paramData = {
        //一些需要的参数
      };
      getRobotAnswer(paramData)
        .then(res => {
          let data = res.data.rep;
          for (let i = 0; i < data.length; i++) {
            let arr = data[i];
            let q = arr.q;
            let a = arr.a;
            this.msgs.push({
              q: q,
              a: a,
              avatarUrl: robotImg,
              self: false,
            });
          }
          this.$refs.chattingContent.scrollTop = this.$refs.chattingContent.scrollHeight;
        })
        .catch(err => {
          console.log(err);
        });
    },
  },
};
</script>
<style lang="css" scoped>
#robot {
  position: absolute;
  bottom: 50px;
  right: 100px;
  width: 40px;
  height: 70px;
  z-index: 9999;
}
#box {
  position: absolute;
  width: 500px;
  height: 450px;
  top: 20%;
  left: 33%;
  z-index: 8888;
  background: rgb(195, 218, 226);
  border-radius: 5px;
  display: none;
}
.uImg,
.rImg {
  width: 40px;
  height: 40px;
}
.container {
  height: 100%;
  border-radius: 5px;
}
#header {
  height: 35px;
  line-height: 35px;
  padding: 6px 15px;
  color: #fff;
  font-size: 20px;
  background: rgb(219, 74, 70);
  border-top-right-radius: 5px;
  border-top-left-radius: 5px;
  display: flex;
  justify-content: space-between;
  user-select: none;
}
.cBox {
  height: 90%;
}
.contents {
  height: 85%;
  overflow: auto;
  display: flex;
  flex-direction: column;
  align-content: flex-start;
  padding: 4px 10px;
  background: #fff;
}
.self {
  float: right;
  height: 40px;
  margin-bottom: 20px;
}
.other {
  position: relative;
}
.clearfix::before {
  display: block;
  content: '';
  clear: both;
}
.answerContent {
  padding: 0 6px;
  line-height: 30px;
  background: rgb(80, 126, 164);
  border-radius: 6px;
  color: #fff;
  margin-left: 50px;
  margin-bottom: 10px;
}
.answerContent p {
  color: #fff;
}
.rImg {
  position: absolute;
  left: 0;
  top: 0;
}
.question {
  padding: 0 6px;
  height: 40px;
  line-height: 40px;
  background: rgb(245, 225, 152);
  border-radius: 6px;
  color: #333;
  margin-right: 10px;
}
img {
  display: inline-block;
}
.userQuestion {
  display: flex;
  justify-content: flex-start;
}
.userInput {
  padding: 10px 10px;
  height: 8%;
  display: flex;
  justify-content: space-between;
}
.tBox {
  width: 80%;
  height: 30px;
  border-radius: 6px;
  background: rgb(242, 245, 242);
}
.ipt {
  width: 100%;
  padding-left: 10px;
  height: 30px;
  line-height: 30px;
  resize: none;
  border: none;
  outline: none;
}
.vp-button--primary {
  width: 15%;
  background: rgb(80, 126, 164);
  color: #fff;
  font-weight: 600;
  line-height: 30px;
  height: 30px;
  padding: 0;
  border: none;
}
</style>

结束。
希望能帮助到大家。

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值