当前位置:   article > 正文

vue + element UI 模拟制作微信页面对话机器人_elementui聊天机器人

elementui聊天机器人
  1. <template>
  2. <div style="height: 100%; width: 100%; background-color: #ededed">
  3. <div class="wrap">
  4. <!-- 头部 -->
  5. <div class="titleBox">
  6. <img
  7. src="https://img-qn-2.51miz.com/preview/element/00/01/27/97/E-1279706-3813EBF9.jpg!/quality/90/unsharp/true/compress/true/format/jpg/fh/320"
  8. alt=""
  9. class="head_portrait"
  10. style="margin-left: 20px; margin-right: 20px"
  11. />
  12. <span style="color: #fff">官方客服</span>
  13. <!-- 在线状态弹框 -->
  14. <el-popover placement="bottom" trigger="click">
  15. <div class="stateBox2" @click="uploadState(1)">
  16. <span class="state1"></span>
  17. <span class="stateText">在线</span>
  18. </div>
  19. <div class="stateBox2" @click="uploadState(2)">
  20. <span class="state2"></span>
  21. <span class="stateText">离线</span>
  22. </div>
  23. <div class="stateBox2" @click="uploadState(3)">
  24. <span class="state3"></span>
  25. <span class="stateText">忙碌</span>
  26. </div>
  27. <div class="stateBox2" @click="uploadState(4)">
  28. <span class="state4"></span>
  29. <span class="stateText">退出</span>
  30. </div>
  31. <div class="stateBox" slot="reference" v-if="state == 1">
  32. <span class="state1"></span>
  33. <span class="stateText">在线</span>
  34. </div>
  35. <div class="stateBox" slot="reference" v-if="state == 2">
  36. <span class="state2"></span>
  37. <span class="stateText">离线</span>
  38. </div>
  39. <div class="stateBox" slot="reference" v-if="state == 3">
  40. <span class="state3"></span>
  41. <span class="stateText">忙碌</span>
  42. </div>
  43. </el-popover>
  44. </div>
  45. <!-- 底部 -->
  46. <div class="infoBox">
  47. <!-- 左边用户列表 -->
  48. <div class="userList">
  49. <div class="searchBox">
  50. <el-input
  51. placeholder="请输入内容"
  52. v-model="search"
  53. class="input-with-select"
  54. size="mini"
  55. @input="inquire"
  56. >
  57. <i
  58. class="el-icon-search el-input__icon"
  59. slot="suffix"
  60. @click="handleIconClick"
  61. >
  62. </i>
  63. </el-input>
  64. <el-button
  65. icon="el-icon-plus"
  66. size="mini"
  67. type="primary"
  68. @click="dialogVisible = true"
  69. ></el-button>
  70. </div>
  71. <div class="userListBox">
  72. <div
  73. v-for="(item, index) in userListData"
  74. :key="index"
  75. @click="getAct(item, index)"
  76. :class="item.id == act ? 'userFlexAct' : 'userFlex'"
  77. >
  78. <div>
  79. <img
  80. :src="item.url"
  81. alt="头像"
  82. class="head_portrait2"
  83. style="margin-left: 20px"
  84. />
  85. </div>
  86. <div style="margin-right: 40px">
  87. <el-tooltip
  88. :content="item.username"
  89. placement="bottom"
  90. effect="light"
  91. >
  92. <div style="color: #565656" class="userName">
  93. {{ item.username }}
  94. </div>
  95. </el-tooltip>
  96. <div class="userInfo">{{ item.info }}</div>
  97. </div>
  98. <div style="margin-right: 10px; font-size: 14px; color: #ccc">
  99. {{ item.timer }}
  100. </div>
  101. </div>
  102. </div>
  103. </div>
  104. <!-- 右边输入框和信息展示 -->
  105. <div class="infoList">
  106. <!-- 信息 -->
  107. <div class="infoTop" ref="scrollBox" id="box">
  108. <div
  109. :class="
  110. item.position == 'left' ? 'chatInfoLeft' : 'chatInfoRight'
  111. "
  112. v-for="(item, index) in userInfoList"
  113. :key="index"
  114. >
  115. <img :src="item.url" alt="头像" class="head_portrait2" />
  116. <div :class="item.position == 'left' ? 'chatLeft' : 'chatRight'">
  117. <div class="text" v-html="item.info"></div>
  118. </div>
  119. </div>
  120. </div>
  121. <!-- 输入框 -->
  122. <div class="infoBottom">
  123. <div class="infoIcon">
  124. <i
  125. @click="extend('照片上传')"
  126. class="el-icon-picture-outline-round"
  127. ></i>
  128. <i @click="extend('发送商品')" class="el-icon-sell"></i>
  129. <i @click="extend('设置')" class="el-icon-setting"></i>
  130. <i @click="extend('聊天记录')" class="el-icon-chat-dot-round"></i>
  131. <i @click="extend('更多选项')" class="el-icon-more-outline"></i>
  132. </div>
  133. <textarea
  134. type="textarea"
  135. class="infoInput"
  136. v-model="textarea"
  137. @keydown.enter.exact="handlePushKeyword($event)"
  138. @keyup.ctrl.enter="lineFeed"
  139. :disabled="isshow == 1 ? false : true"
  140. />
  141. <div
  142. class="fasong"
  143. @click="setUp"
  144. v-show="isshow == 1 ? true : false"
  145. >
  146. 发送
  147. </div>
  148. </div>
  149. </div>
  150. </div>
  151. </div>
  152. <!-- 搜索框边 + 号弹框 -->
  153. <el-dialog
  154. title="选择需要添加的联系人"
  155. :visible.sync="dialogVisible"
  156. width="30%"
  157. :modal="false"
  158. >
  159. <span>自定义页面,还没想好写什么功能</span>
  160. <span slot="footer" class="dialog-footer">
  161. <el-button @click="dialogVisible = false">取 消</el-button>
  162. <el-button type="primary" @click="dialogVisible = false"
  163. >确 定</el-button
  164. >
  165. </span>
  166. </el-dialog>
  167. </div>
  168. </template>
  169. <script>
  170. export default {
  171. data() {
  172. return {
  173. //websocket部分
  174. path: "ws://localhost:8888", //后台的websocket地址,找后端要
  175. ws: null, //建立的连接
  176. lockReconnect: false, //是否真正建立连接
  177. timeout: 10 * 1000, //30秒一次心跳
  178. timeoutObj: null, //心跳心跳倒计时
  179. serverTimeoutObj: null, //心跳倒计时
  180. timeoutnum: null, //断开 重连倒计时
  181. // 在线状态
  182. state: 1,
  183. //搜索用户
  184. search: "",
  185. //用户列表渲染数据
  186. userListData: [
  187. {
  188. id: 0,
  189. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  190. username: "花间一壶酒",
  191. info: "在吗",
  192. timer: "2022/8/9",
  193. },
  194. {
  195. id: 1,
  196. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  197. username: "超人不换内裤",
  198. info: "在吗在吗",
  199. timer: "2022/8/1",
  200. },
  201. {
  202. id: 2,
  203. url: "https://img1.baidu.com/it/u=2029513305,2137933177&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=472",
  204. username: "王子变蛤蟆",
  205. info: "你好,在吗",
  206. timer: "2022/8/9",
  207. },
  208. {
  209. id: 3,
  210. url: "https://img1.baidu.com/it/u=1960292808,1761809160&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  211. username: "皇太子",
  212. info: "给我看一下这个情况呢",
  213. timer: "2022/8/2",
  214. },
  215. {
  216. id: 4,
  217. url: "https://img2.baidu.com/it/u=3684117954,695988885&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400",
  218. username: "飞天小女警",
  219. info: "模拟数据发送一下呢",
  220. timer: "2022/8/4",
  221. },
  222. {
  223. id: 5,
  224. url: "https://img2.baidu.com/it/u=4122738859,2522601053&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  225. username: "罗大大大",
  226. info: "在吗",
  227. timer: "2022/8/5",
  228. },
  229. {
  230. id: 6,
  231. url: "https://img0.baidu.com/it/u=661161858,172661768&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  232. username: "王子花",
  233. info: "好的,我知道了",
  234. timer: "2022/8/9",
  235. },
  236. {
  237. id: 7,
  238. url: "https://img2.baidu.com/it/u=835899845,548435859&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  239. username: "开盖有奖",
  240. info: "试试看吧,再发一次",
  241. timer: "2022/8/9",
  242. },
  243. {
  244. id: 8,
  245. url: "https://img0.baidu.com/it/u=4065107391,2142799144&fm=253&fmt=auto&app=138&f=JPEG?w=527&h=500",
  246. username: "日本大爆炸",
  247. info: "在吗",
  248. timer: "2022/8/5",
  249. },
  250. {
  251. id: 9,
  252. url: "https://img2.baidu.com/it/u=2860188096,638334621&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  253. username: "秋天的第一杯奶茶",
  254. info: "好的,我知道了",
  255. timer: "2022/8/9",
  256. },
  257. {
  258. id: 10,
  259. url: "https://img0.baidu.com/it/u=1694074520,2517635995&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  260. username: "潮汐",
  261. info: "试试看吧,再发一次",
  262. timer: "2022/8/9",
  263. },
  264. ],
  265. //用户列表筛选数据
  266. userListDatas: [
  267. {
  268. id: 0,
  269. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  270. username: "花间一壶酒",
  271. info: "在吗",
  272. timer: "2022/8/9",
  273. },
  274. {
  275. id: 1,
  276. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  277. username: "超人不换内裤",
  278. info: "在吗在吗",
  279. timer: "2022/8/1",
  280. },
  281. {
  282. id: 2,
  283. url: "https://img1.baidu.com/it/u=2029513305,2137933177&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=472",
  284. username: "王子变蛤蟆",
  285. info: "你好,在吗",
  286. timer: "2022/8/9",
  287. },
  288. {
  289. id: 3,
  290. url: "https://img1.baidu.com/it/u=1960292808,1761809160&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  291. username: "皇太子",
  292. info: "给我看一下这个情况呢",
  293. timer: "2022/8/2",
  294. },
  295. {
  296. id: 4,
  297. url: "https://img2.baidu.com/it/u=3684117954,695988885&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400",
  298. username: "飞天小女警",
  299. info: "模拟数据发送一下呢",
  300. timer: "2022/8/4",
  301. },
  302. {
  303. id: 5,
  304. url: "https://img2.baidu.com/it/u=4122738859,2522601053&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  305. username: "罗大大大",
  306. info: "在吗",
  307. timer: "2022/8/5",
  308. },
  309. {
  310. id: 6,
  311. url: "https://img0.baidu.com/it/u=661161858,172661768&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  312. username: "王子花",
  313. info: "好的,我知道了",
  314. timer: "2022/8/9",
  315. },
  316. {
  317. id: 7,
  318. url: "https://img2.baidu.com/it/u=835899845,548435859&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  319. username: "开盖有奖",
  320. info: "试试看吧,再发一次",
  321. timer: "2022/8/9",
  322. },
  323. {
  324. id: 8,
  325. url: "https://img0.baidu.com/it/u=4065107391,2142799144&fm=253&fmt=auto&app=138&f=JPEG?w=527&h=500",
  326. username: "日本大爆炸",
  327. info: "在吗",
  328. timer: "2022/8/5",
  329. },
  330. {
  331. id: 9,
  332. url: "https://img2.baidu.com/it/u=2860188096,638334621&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  333. username: "秋天的第一杯奶茶",
  334. info: "好的,我知道了",
  335. timer: "2022/8/9",
  336. },
  337. {
  338. id: 10,
  339. url: "https://img0.baidu.com/it/u=1694074520,2517635995&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  340. username: "潮汐",
  341. info: "试试看吧,再发一次",
  342. timer: "2022/8/9",
  343. },
  344. ],
  345. //用户点击选中变色
  346. act: Number,
  347. // 加号弹框
  348. dialogVisible: false,
  349. //模拟花间一壶酒用户的历史信息
  350. userInfoList2: [
  351. {
  352. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  353. username: "花间一壶酒",
  354. info: "在吗",
  355. timer: "2022/8/9",
  356. position: "left",
  357. },
  358. {
  359. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  360. username: "花间一壶酒",
  361. info: "在吗",
  362. timer: "2022/8/9",
  363. position: "left",
  364. },
  365. {
  366. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  367. username: "超人不换内裤",
  368. info: "在吗在吗",
  369. timer: "2022/8/1",
  370. position: "right",
  371. },
  372. {
  373. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  374. username: "花间一壶酒",
  375. info: "在吗",
  376. timer: "2022/8/9",
  377. position: "left",
  378. },
  379. {
  380. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  381. username: "超人不换内裤",
  382. info: "在吗在吗",
  383. timer: "2022/8/1",
  384. position: "right",
  385. },
  386. {
  387. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  388. username: "花间一壶酒",
  389. info: "在吗在吗在吗在吗在吗在吗在吗",
  390. timer: "2022/8/9",
  391. position: "left",
  392. },
  393. {
  394. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  395. username: "超人不换内裤",
  396. info: "在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗",
  397. timer: "2022/8/1",
  398. position: "right",
  399. },
  400. {
  401. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  402. username: "花间一壶酒",
  403. info: "在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗在吗",
  404. timer: "2022/8/9",
  405. position: "left",
  406. },
  407. {
  408. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  409. username: "超人不换内裤",
  410. info: "在吗在吗在吗在吗在吗在吗",
  411. timer: "2022/8/1",
  412. position: "right",
  413. },
  414. ],
  415. //模拟超人不换内裤用户的历史信息
  416. userInfoList3: [
  417. {
  418. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  419. username: "花间一壶酒",
  420. info: "测试测试",
  421. timer: "2022/8/9",
  422. position: "left",
  423. },
  424. {
  425. url: "https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
  426. username: "花间一壶酒",
  427. info: "测试",
  428. timer: "2022/8/9",
  429. position: "left",
  430. },
  431. {
  432. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  433. username: "超人不换内裤",
  434. info: "测试测试测试测试",
  435. timer: "2022/8/1",
  436. position: "right",
  437. },
  438. ],
  439. //历史信息
  440. userInfoList: [],
  441. //输入框
  442. textarea: "",
  443. //滚动条距离顶部距离
  444. scrollTop: 0,
  445. //发送和输入显隐
  446. isshow: 0,
  447. };
  448. },
  449. created() {
  450. this.initWebpack();
  451. },
  452. beforeDestroy() {
  453. // 离开页面后关闭连接
  454. this.ws.close();
  455. // 清除时间
  456. clearTimeout(this.timeoutObj);
  457. clearTimeout(this.serverTimeoutObj);
  458. },
  459. methods: {
  460. //切换客服状态
  461. uploadState(state) {
  462. if (state !== 4) {
  463. this.state = state;
  464. } else {
  465. this.$confirm("是否退出登录?", "提示", {
  466. confirmButtonText: "确定",
  467. cancelButtonText: "取消",
  468. type: "warning",
  469. })
  470. .then(() => {
  471. this.$message({
  472. type: "success",
  473. message: "退出成功!",
  474. });
  475. })
  476. .catch(() => {
  477. this.$message({
  478. type: "info",
  479. message: "已取消退出",
  480. });
  481. });
  482. }
  483. },
  484. //搜索icon
  485. handleIconClick() {
  486. console.log(1);
  487. },
  488. //点击用户
  489. getAct(val, index) {
  490. this.isshow = 1;
  491. // 点击用户切换数据时先清除监听滚动事件,防止出现没有历史数据的用户,滚动条为0,会触发滚动事件
  492. this.$refs.scrollBox.removeEventListener("scroll", this.srTop);
  493. //点击变色
  494. this.act = val.id;
  495. //清空消息数组
  496. this.userInfoList = [];
  497. // 模拟一下点击用户出现历史记录的样子,实际开发中是axios请求后数组赋值然后调用setPageScrollTo
  498. if (val.username == "花间一壶酒") {
  499. this.userInfoList = this.userInfoList2;
  500. // 直接调用不生效:因为你历史数据刚给,渲染的时候盒子高度还没有成型,所以直接调用拿不到,用个定时器让他在下一轮循环中调用,盒子就已经生成了
  501. this.$nextTick(() => { // 一定要用nextTick
  502. this.setPageScrollTo();
  503. //页面滚动条距离顶部高度等于这个盒子的高度
  504. this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
  505. })
  506. } else if (val.username == "超人不换内裤") {
  507. this.userInfoList = this.userInfoList3;
  508. // 直接调用不生效:因为你历史数据刚给,渲染的时候盒子高度还没有成型,所以直接调用拿不到,用个定时器让他在下一轮循环中调用,盒子就已经生成了
  509. this.$nextTick(() => { // 一定要用nextTick
  510. this.setPageScrollTo();
  511. //页面滚动条距离顶部高度等于这个盒子的高度
  512. this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
  513. })
  514. } else if (val.username == "王子变蛤蟆") {
  515. this.$nextTick(() => { // 一定要用nextTick
  516. this.setPageScrollTo();
  517. //页面滚动条距离顶部高度等于这个盒子的高度
  518. this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
  519. })
  520. }
  521. },
  522. // 模糊搜索用户
  523. inquire() {
  524. let fuzzy = this.search;
  525. if (fuzzy) {
  526. this.userListData = this.userListDatas.filter((item) => {
  527. return item.username.includes(fuzzy);
  528. });
  529. } else {
  530. this.userListData = this.userListDatas;
  531. }
  532. },
  533. //发送
  534. setUp() {
  535. console.log("发送内容:", this.textarea);
  536. this.userInfoList2.push({
  537. url: "https://img2.baidu.com/it/u=2859542338,3761174075&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500",
  538. username: "超人不换内裤",
  539. info: this.textarea,
  540. timer: "2022/8/1",
  541. position: "right",
  542. });
  543. this.textarea = "";
  544. // 页面滚动到底部
  545. this.$nextTick(() => { // 一定要用nextTick
  546. this.setPageScrollTo();
  547. //页面滚动条距离顶部高度等于这个盒子的高度
  548. this.$refs.scrollBox.scrollTop = this.$refs.scrollBox.scrollHeight;
  549. })
  550. },
  551. // 监听键盘回车阻止换行并发送
  552. handlePushKeyword(event) {
  553. console.log(event);
  554. if (event.keyCode === 13) {
  555. event.preventDefault(); // 阻止浏览器默认换行操作
  556. this.setUp(); //发送文本
  557. return false;
  558. }
  559. },
  560. // 监听按的是ctrl + 回车,就换行
  561. lineFeed() {
  562. console.log("换行");
  563. this.textarea = this.textarea + "\n";
  564. },
  565. //点击icon
  566. extend(val) {
  567. alert("你点击了:" + val);
  568. },
  569. //滚动条默认滚动到最底部
  570. setPageScrollTo(s, c) {
  571. //获取中间内容盒子的可见区域高度
  572. this.scrollTop = document.querySelector("#box").offsetHeight;
  573. setTimeout((res) => {
  574. //加个定时器,防止上面高度没获取到,再获取一遍。
  575. if (this.scrollTop != this.$refs.scrollBox.offsetHeight) {
  576. this.scrollTop = document.querySelector("#box").offsetHeight;
  577. }
  578. }, 100);
  579. //scrollTop:滚动条距离顶部的距离。
  580. //把上面获取到的高度座位距离,把滚动条顶到最底部
  581. this.$refs.scrollBox.scrollTop = this.scrollTop;
  582. //判断是否有滚动条,有滚动条就创建一个监听滚动事件,滚动到顶部触发srTop方法
  583. if (this.$refs.scrollBox.scrollTop > 0) {
  584. this.$refs.scrollBox.addEventListener("scroll", this.srTop);
  585. }
  586. },
  587. //滚动条到达顶部
  588. srTop() {
  589. //判断:当滚动条距离顶部为0时代表滚动到顶部了
  590. if (this.$refs.scrollBox.scrollTop == 0) {
  591. //逻辑简介:
  592. //到顶部后请求后端的方法,获取第二页的聊天记录,然后插入到现在的聊天数据前面。
  593. //如何插入前面:可以先把获取的数据保存在 A 变量内,然后 this.userInfoList=A.concat(this.userInfoList)把数组合并进来就可以了
  594. //拿聊天记录逻辑:
  595. //第一次调用一个请求拉历史聊天记录,发请求时参数带上页数 1 传过去,拿到的就是第一页的聊天记录,比如一次拿20条。你显示出来
  596. //然后向上滚动到顶部时,触发新的请求,在请求中把分页数先 +1 然后再请求,这就拿到了第二页数据,然后通过concat合并数组插入进前面,依次类推,功能完成!
  597. alert("已经到顶部了");
  598. }
  599. },
  600. //-----------------------以下是websocket部分方法
  601. // 初始化websocket链接
  602. initWebpack() {
  603. if (typeof WebSocket === "undefined") {
  604. alert("您的浏览器不支持socket");
  605. } else {
  606. this.ws = new WebSocket(this.path); //实例
  607. this.ws.onopen = this.onopen; //监听链接成功
  608. this.ws.onmessage = this.onmessage; //监听后台返回消息
  609. this.ws.onclose = this.onclose; //监听链接关闭
  610. this.ws.onerror = this.onerror; //监听链接异常
  611. }
  612. },
  613. //重新连接
  614. reconnect() {
  615. var that = this;
  616. if (that.lockReconnect) {
  617. return;
  618. }
  619. that.lockReconnect = true;
  620. //没连接上会一直重连,设置延迟避免请求过多
  621. that.timeoutnum && clearTimeout(that.timeoutnum);
  622. that.timeoutnum = setTimeout(function () {
  623. that.initWebpack(); //新连接
  624. that.lockReconnect = false;
  625. }, 5000);
  626. },
  627. //重置心跳
  628. reset() {
  629. var that = this;
  630. clearTimeout(that.timeoutObj); //清除心跳倒计时
  631. clearTimeout(that.serverTimeoutObj); //清除超时关闭倒计时
  632. that.start(); //重启心跳
  633. },
  634. //开启心跳
  635. start() {
  636. var self = this;
  637. self.timeoutObj && clearTimeout(self.timeoutObj); //心跳倒计时如果有值就清除掉,防止重复
  638. self.serverTimeoutObj && clearTimeout(self.serverTimeoutObj); //超时关闭倒计时如果有值就清除掉,防止重复
  639. //然后从新开一个定时器
  640. self.timeoutObj = setTimeout(function () {
  641. //这里通过readyState判断链接状态,有四个值,0:正在连接,1:已连接,2:正在断开,3:已经断开或者链接不成功
  642. if (self.ws.readyState == 1) {
  643. //如果连接正常,给后天发送一个值,可以自定义,然后后台返回我们一个信息,我们接收到后会触发onmessage方法回调
  644. self.ws.send("ping");
  645. } else {
  646. //如果检测readyState不等于1那也就代表不处在链接状态,那就是不正常的,那就调用重连方法
  647. self.reconnect();
  648. }
  649. //从新赋值一个超时计时器,这个定时器的作用:当你触发心跳的时候可能会出现一个情况,后台崩了,前台发了个心跳,没有回应,就不会触发onmessage方法
  650. //所以我们需要在这个心跳发送出去了后,再开一个定时器,用于监控心跳返回的时间,比如10秒,那么10秒内如果后台回我了,触发onmessage方法,自然就会把心跳时间和超时倒计时一起清空掉
  651. //也就不会触发这个关闭连接,但是如果10秒后还是没有收到回应,那么就会触发关闭连接,而关闭连接方法内又会触发重连方法,循环就走起来了。
  652. self.serverTimeoutObj = setTimeout(function () {
  653. //如果超时了就关闭连接
  654. self.ws.close();
  655. }, self.timeout);
  656. }, self.timeout);
  657. },
  658. //连接成功
  659. onopen() {
  660. console.log("连接成功");
  661. if (this.ws.readyState == 1) {
  662. //如果连接正常,给后天发送一个值,可以自定义,然后后台返回我们一个信息,我们接收到后会触发onmessage方法回调
  663. this.ws.send(
  664. "链接上啦!!!"
  665. );
  666. }
  667. this.start(); //链接成功后开启心跳
  668. },
  669. //接受后台信息回调
  670. onmessage(e) {
  671. /**这里写自己的业务逻辑代码**/
  672. console.log("收到后台信息:", e.data);
  673. this.reset(); //收到服务器信息,心跳重置
  674. },
  675. //关闭连接回调
  676. onclose(e) {
  677. console.log("连接关闭");
  678. this.reconnect(); //重连
  679. },
  680. //连接异常回调
  681. onerror(e) {
  682. console.log("出现错误");
  683. this.reconnect(); //重连
  684. },
  685. },
  686. };
  687. </script>
  688. <style scoped>
  689. .wrap {
  690. height: 80%;
  691. width: 55%;
  692. background-color: #f2f2f2;
  693. margin: auto;
  694. transform: translateY(10%);
  695. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
  696. border-radius: 10px;
  697. }
  698. .titleBox {
  699. height: 10%;
  700. width: 100%;
  701. background-image: linear-gradient(to right, #1e76bc, #69a3d5);
  702. display: flex;
  703. align-items: center;
  704. border-top-right-radius: 10px;
  705. border-top-left-radius: 10px;
  706. }
  707. .infoBottom {
  708. height: 30%;
  709. display: flex;
  710. flex-direction: column;
  711. }
  712. /* 输入框 */
  713. .infoInput {
  714. height: 58%;
  715. width: 100%;
  716. border: none;
  717. resize: none;
  718. padding: 10px;
  719. box-sizing: border-box;
  720. background-color: #f2f2f2;
  721. color: #434343;
  722. }
  723. .fasong {
  724. height: 30px;
  725. width: 80px;
  726. background-color: #e8e8e8;
  727. text-align: center;
  728. line-height: 30px;
  729. border-radius: 4px;
  730. color: #58df4d;
  731. margin-top: 1%;
  732. align-self: flex-end;
  733. margin-right: 20px;
  734. cursor: pointer;
  735. }
  736. .infoIcon {
  737. height: 40px;
  738. width: 100%;
  739. display: flex;
  740. align-items: center;
  741. }
  742. .infoIcon i {
  743. font-size: 24px;
  744. color: #676767;
  745. margin-left: 15px;
  746. cursor: pointer;
  747. }
  748. /* 头像 */
  749. .head_portrait {
  750. width: 3rem;
  751. height: 3rem;
  752. border-radius: 50%;
  753. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
  754. }
  755. .head_portrait2 {
  756. width: 3rem;
  757. height: 3rem;
  758. border-radius: 50%;
  759. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
  760. }
  761. .stateBox {
  762. margin-left: 20px;
  763. padding: 1px 8px;
  764. background-color: #fff;
  765. border-radius: 10px;
  766. text-align: center;
  767. cursor: pointer;
  768. }
  769. .stateBox2 {
  770. margin-left: 20px;
  771. padding: 1px 8px;
  772. background-color: #fff;
  773. border-radius: 10px;
  774. text-align: center;
  775. cursor: pointer;
  776. }
  777. .stateBox2:hover {
  778. background-color: #dcdcdc;
  779. }
  780. /* 在线 */
  781. .state1 {
  782. display: inline-block;
  783. height: 10px;
  784. width: 10px;
  785. border-radius: 50%;
  786. background-color: #8ee80e;
  787. }
  788. /* 离线 */
  789. .state2 {
  790. display: inline-block;
  791. height: 10px;
  792. width: 10px;
  793. border-radius: 50%;
  794. background-color: #cacaca;
  795. }
  796. /* 忙碌 */
  797. .state3 {
  798. display: inline-block;
  799. height: 10px;
  800. width: 10px;
  801. border-radius: 50%;
  802. background-color: #ff8c1e;
  803. }
  804. /* 退出登录 */
  805. .state4 {
  806. display: inline-block;
  807. height: 10px;
  808. width: 10px;
  809. border-radius: 50%;
  810. background-color: #7e7e7e;
  811. }
  812. .stateText {
  813. font-size: 14px;
  814. margin-left: 5px;
  815. }
  816. /* 列表和信息 */
  817. .infoBox {
  818. height: 90%;
  819. width: 100%;
  820. display: flex;
  821. }
  822. /* 用户列表大盒子 */
  823. .userList {
  824. height: 100%;
  825. width: 300px;
  826. border-right: 1px solid #ccc;
  827. display: flex;
  828. flex-direction: column;
  829. }
  830. /* 用户列表 */
  831. .userListBox {
  832. flex: 1;
  833. width: 100%;
  834. overflow: auto;
  835. }
  836. /* 信息外层盒子 */
  837. .infoList {
  838. height: 100%;
  839. width: 72%;
  840. }
  841. /* 信息列表 */
  842. .infoTop {
  843. height: 70%;
  844. width: 100%;
  845. border-bottom: 1px solid #ccc;
  846. padding: 10px;
  847. box-sizing: border-box;
  848. overflow: auto;
  849. }
  850. /* 对方发的信息样式 */
  851. .chatInfoLeft {
  852. min-height: 70px;
  853. margin-left: 10px;
  854. margin-top: 10px;
  855. display: flex;
  856. }
  857. .chatLeft {
  858. margin-left: 15px;
  859. flex: 1;
  860. }
  861. .chatLeft .text {
  862. color: #434343;
  863. margin-top: 8px;
  864. background-color: #e3e3e3;
  865. display: inline-block;
  866. padding: 6px 10px;
  867. border-radius: 10px;
  868. max-width: 50%;
  869. /* 忽略多余的空白,只保留一个空白 */
  870. white-space: normal;
  871. /* 换行显示全部字符 */
  872. word-break: break-all;
  873. white-space: pre-wrap;
  874. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
  875. }
  876. /* 自己发的信息样式 */
  877. .chatInfoRight {
  878. height: 70px;
  879. margin-left: 10px;
  880. margin-top: 10px;
  881. display: flex;
  882. flex-direction: row-reverse;
  883. }
  884. .chatRight {
  885. margin-right: 15px;
  886. flex: 1;
  887. /* 用align-items把元素靠右对齐 */
  888. display: flex;
  889. flex-direction: column;
  890. align-items: flex-end;
  891. }
  892. .chatRight .text {
  893. color: #434343;
  894. margin-top: 8px;
  895. background-color: #95ec69;
  896. display: inline-block;
  897. padding: 6px 10px;
  898. border-radius: 10px;
  899. max-width: 50%;
  900. /* 忽略多余的空白,只保留一个空白 */
  901. white-space: normal;
  902. /* 换行显示全部字符 */
  903. word-break: break-all;
  904. white-space: pre-wrap;
  905. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
  906. }
  907. .searchBox {
  908. padding: 4px 2px;
  909. border-bottom: 1px solid #ededed;
  910. }
  911. .input-with-select {
  912. width: 80%;
  913. margin-right: 2%;
  914. }
  915. /* 点击用户变色 */
  916. .userFlexAct {
  917. display: flex;
  918. justify-content: space-between;
  919. align-items: center;
  920. height: 70px;
  921. border-bottom: 1px solid #e8e8e8;
  922. cursor: pointer;
  923. background-color: #e8e8e8;
  924. }
  925. /* 用户默认颜色 */
  926. .userFlex {
  927. display: flex;
  928. justify-content: space-between;
  929. align-items: center;
  930. height: 70px;
  931. border-bottom: 1px solid #e8e8e8;
  932. cursor: pointer;
  933. }
  934. /* 用户名 */
  935. .userName {
  936. width: 100px;
  937. white-space: nowrap;
  938. overflow: hidden;
  939. text-overflow: ellipsis;
  940. }
  941. /* 简略信息 */
  942. .userInfo {
  943. width: 100px;
  944. font-size: 14px;
  945. color: #ccc;
  946. white-space: nowrap;
  947. overflow: hidden;
  948. text-overflow: ellipsis;
  949. margin-top: 3px;
  950. }
  951. /* 滚动条样式 */
  952. ::-webkit-scrollbar {
  953. width: 5px;
  954. height: 10px;
  955. }
  956. ::-webkit-scrollbar-thumb {
  957. background-color: #dbd9d9;
  958. border-radius: 3px;
  959. }
  960. </style>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/761304
推荐阅读
相关标签
  

闽ICP备14008679号