ChatFrame.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <template>
  2. <div>
  3. <!-- v-if="selected" -->
  4. <div class="chat-wrap">
  5. <div class="chat-title">
  6. test
  7. <!-- {{ selected.name || selected.roomname }} -->
  8. </div>
  9. <div class="record-warp">
  10. <el-scrollbar
  11. v-if="tab === 'friends'"
  12. style="height:95%;"
  13. ref="myScrollbar"
  14. >
  15. <div
  16. class="record-list"
  17. v-for="(item, i) in friend_record_list"
  18. :key="i"
  19. >
  20. <div
  21. class="record-item"
  22. :class="item.id !== self.id ? 'receive-record' : 'send-record'"
  23. >
  24. <div>
  25. <span>{{ item.msg }}</span>
  26. </div>
  27. <div>
  28. <span>{{ item.msg }}</span>
  29. </div>
  30. <div>
  31. <span>{{ item.msg }}</span>
  32. </div>
  33. </div>
  34. </div>
  35. </el-scrollbar>
  36. <el-scrollbar
  37. v-if="tab === 'groups'"
  38. style="height:95%"
  39. ref="myScrollbar"
  40. >
  41. <div
  42. class="record-list"
  43. v-for="(item, i) in room_record_list"
  44. :key="i"
  45. >
  46. <div class="record-item" v-if="item.id !== self.id">
  47. <div class="avatar">
  48. <el-avatar
  49. shape="square"
  50. src="https://img2.baidu.com/it/u=1077360284,2857506492&fm=26&fmt=auto&gp=0.jpg"
  51. ></el-avatar>
  52. </div>
  53. <div class="group-record-item">
  54. <div class="nickname">
  55. {{ item.nickname }}
  56. </div>
  57. <!-- <div class="user">5asfdasdf</div> -->
  58. <!-- <span>{{ item.msg }}</span> -->
  59. <div class="message-content-text" style="margin-left:2px; margin-top: 0" v-text="_parseText(item.msg)"></div>
  60. </div>
  61. </div>
  62. <div v-if="item.id === self.id" class="record-item send-record">
  63. <!-- <div class="user">5asfdasdf</div> -->
  64. <!-- <span>{{ item.msg }}</span> -->
  65. <!-- 链接 -->
  66. <!-- <a class="message-content-text" href="https://img2.baidu.com/it/u=1605987499,4009355285&fm=26&fmt=auto&gp=0.jpg" target="_blank">https://img2.baidu.com/it/u=1605987499,4009355285&fm=26&fmt=auto&gp=0.jpg</a> -->
  67. <!-- 文本 -->
  68. <!-- <div class="message-content-text" v-text="_parseText(item.msg)"></div> -->
  69. <!-- 图片 -->
  70. <!-- <div class="message-content-image" :style="getImageStyle('https://img2.baidu.com/it/u=1605987499,4009355285&fm=26&fmt=auto&gp=0.jpg')">
  71. <viewer style="display: flex; align-items: center">
  72. <img src="https://img2.baidu.com/it/u=1605987499,4009355285&fm=26&fmt=auto&gp=0.jpg" alt="" />
  73. </viewer>
  74. </div> -->
  75. <!-- 视频格式文件 -->
  76. <div class="message-content-image">
  77. <video style="width: 255px" src="https://media.w3.org/2010/05/sintel/trailer.mp4" controls="controls">您的浏览器不支持 video 标签。</video>
  78. </div>
  79. <!-- <div class="avatar">
  80. <el-avatar
  81. shape="square"
  82. src="https://img2.baidu.com/it/u=1077360284,2857506492&fm=26&fmt=auto&gp=0.jpg"
  83. ></el-avatar>
  84. </div> -->
  85. </div>
  86. </div>
  87. </el-scrollbar>
  88. </div>
  89. <div class="send-msg-wrap">
  90. <div class="send-header">
  91. <div class="tools" style="margin:10px">
  92. <el-upload
  93. ref="upload"
  94. class="file-uploader"
  95. action="/api/upload/"
  96. :show-file-list="false"
  97. :on-success="handleSuccess"
  98. :before-upload="beforeUpload"
  99. style="display: inline"
  100. >
  101. <i
  102. id="upload-btn"
  103. class="el-icon-folder-opened"
  104. @click="upload"
  105. ></i>
  106. </el-upload>
  107. <i class="el-icon-folder-add"></i>
  108. <i class="el-icon-folder-opened"></i>
  109. </div>
  110. </div>
  111. <div class="input-box">
  112. <textarea
  113. v-model="msg"
  114. placeholder="请输入聊天内容"
  115. @keyup.enter="sendMsg"
  116. ></textarea>
  117. <div class="send-footer">
  118. <el-button
  119. type="info"
  120. plain
  121. class="send-btn"
  122. size="small"
  123. @click="sendMsg"
  124. >发送</el-button
  125. >
  126. </div>
  127. </div>
  128. </div>
  129. </div>
  130. <div class="chat-wrap" v-if="!selected">
  131. <el-empty description="请选择聊天对象"></el-empty>
  132. </div>
  133. </div>
  134. </template>
  135. <script>
  136. import { addCookie, getCookie } from "@/utils/setCookie.js";
  137. import { isUrl, parseText, processReturn } from "@/utils/common";
  138. export default {
  139. name: "ChatFrame",
  140. props: ["selected", "tab"],
  141. data() {
  142. return {
  143. msg: "",
  144. friend_record: [],
  145. friend_record_list: [],
  146. room_record: {},
  147. room_record_list: [],
  148. self: null,
  149. uploadProgress: false
  150. };
  151. },
  152. computed: {},
  153. watch: {
  154. selected(n, o) {
  155. if (this.friend_record[this.selected.id]) {
  156. this.friend_record_list = this.friend_record[this.selected.id];
  157. }
  158. if (this.room_record[this.selected.id]) {
  159. this.room_record_list = this.room_record[this.selected.id];
  160. }
  161. this.$nextTick(() => {
  162. this.$refs["myScrollbar"].wrap.scrollTop = this.$refs[
  163. "myScrollbar"
  164. ].wrap.scrollHeight;
  165. });
  166. }
  167. },
  168. methods: {
  169. handleSuccess(res, file) {
  170. console.log(`res`, res);
  171. this.imageUrl = URL.createObjectURL(file.raw);
  172. },
  173. beforeUpload(file) {
  174. console.log(`file`, file);
  175. // const isJPG = file.type === "image/jpeg";
  176. // const isLt2M = file.size / 1024 / 1024 < 2;
  177. // if (!isJPG) {
  178. // this.$message.error("上传头像图片只能是 JPG 格式!");
  179. // }
  180. // if (!isLt2M) {
  181. // this.$message.error("上传头像图片大小不能超过 2MB!");
  182. // }
  183. // return isJPG && isLt2M;
  184. this.uploadProgress = true;
  185. this.fileData = file;
  186. // 调用函数分割文件 我这里是分割成不超过20M的文件快
  187. this.fileDataList = this.createFileChunk(file, 1024 * 1024 * 20);
  188. console.log(`this.fileDataList`, this.fileDataList);
  189. // return new Promise((reslove, reject)=>{
  190. // this.fileUpLoad(reslove, reject);
  191. // })
  192. let fd = new FormData();
  193. fd.append("filename", file);
  194. // fd.append('project_id', this.project_id)
  195. // fd.append('version_id', this.version_id)
  196. console.log(`fd`, fd);
  197. this.$http.post("/api/upload/", fd).then(
  198. res => {
  199. // this.importDataBtnText='导入成功';
  200. console.log(`res1111`, res);
  201. console.log("======success====");
  202. },
  203. res => {
  204. console.log("===========shibai====");
  205. // this.importDataBtnText='导入失败';
  206. console.log(res);
  207. }
  208. );
  209. return false;
  210. },
  211. submitUpload() {
  212. this.$refs.upload.submit();
  213. },
  214. /**
  215. * 文本转译/校验
  216. * @params text
  217. */
  218. _parseText(text) {
  219. return parseText(text);
  220. },
  221. /**
  222. * 是否URL
  223. * @params text
  224. */
  225. _isUrl(text) {
  226. return isUrl(text);
  227. },
  228. // // 初始化上传接口的函数,再上面上传之前调用的
  229. // fileUpLoad(reslove, reject) {
  230. // const paramsData = {
  231. // multipartUpload: true, // 是否是文件分步,分块上传
  232. // name: this.fileData.name,
  233. // size: this.fileData.size,
  234. // }
  235. // initFileUpload(paramsData).then(res => {
  236. // const uploadUrl = res.data.data.uploadUrl
  237. // const completeMultipartUrl = res.data.data.completeMultipartUrl
  238. // // 文件上传的请求地址
  239. // this.uploadUrl = uploadUrl
  240. // // 合并文件上传的请求地址
  241. // this.completeMultipartUrl = completeMultipartUrl
  242. // reslove();
  243. // }).catch((err)=>{
  244. // reject(err)
  245. // })
  246. // },
  247. // 文件分割的方法
  248. createFileChunk(file, size = chunkSize) {
  249. const fileChunkList = [];
  250. let count = 0;
  251. let num = 1;
  252. while (count < file.size) {
  253. fileChunkList.push({
  254. file: file.slice(count, count + size),
  255. partNumber: num
  256. });
  257. count += size;
  258. num++;
  259. }
  260. return fileChunkList;
  261. },
  262. /**
  263. * 根据图片url设置图片框宽高, 注意是图片框
  264. */
  265. getImageStyle(src) {
  266. const arr = src.split('$');
  267. let width = Number(arr[2]);
  268. let height = Number(arr[3]);
  269. if (this.mobile) {
  270. // 如果是移动端,图片最大宽度138, 返回值加12是因为设置的是图片框的宽高要加入padding值
  271. if (width > 138) {
  272. height = (height * 138) / width;
  273. width = 138;
  274. return {
  275. width: `${width + 12}px`,
  276. height: `${height + 12}px`,
  277. };
  278. }
  279. }
  280. return {
  281. width: `${width + 12}px`,
  282. height: `${height + 12}px`,
  283. };
  284. },
  285. upload() {},
  286. sendMsg(e) {
  287. if (e.which === 13) {
  288. console.log(`e.which`, e.which);
  289. e.cancelBubble = true;
  290. e.preventDefault();
  291. e.stopPropagation();
  292. }
  293. if (this.msg.trim() === "") {
  294. this.msg = "";
  295. return;
  296. }
  297. if (!this.self) {
  298. this.self = JSON.parse(localStorage.getItem("self"));
  299. }
  300. console.log(`this.self`, this.self);
  301. const msgInfo = {
  302. id: this.$socket.id,
  303. msg: this.msg,
  304. nickname: this.self.nickname
  305. };
  306. this.msg = ""; // 输入enter后置空
  307. if (this.tab === "friends") {
  308. msgInfo.toid = this.selected.id;
  309. this.$socket.emit("private message", this.selected.id, msgInfo);
  310. if (this.friend_record[this.selected.id]) {
  311. this.friend_record[this.selected.id].push(msgInfo);
  312. this.friend_record_list = this.friend_record[this.selected.id];
  313. } else {
  314. this.friend_record[this.selected.id] = [];
  315. this.friend_record[this.selected.id].push(msgInfo);
  316. this.friend_record_list = this.friend_record[this.selected.id];
  317. }
  318. addCookie("friend_record", JSON.stringify(this.friend_record));
  319. } else {
  320. msgInfo.roomid = this.selected.id;
  321. this.$socket.emit("chat-room", this.selected.id, msgInfo);
  322. if (this.room_record[this.selected.id]) {
  323. this.room_record[this.selected.id].push(msgInfo);
  324. this.room_record_list = this.room_record[this.selected.id];
  325. } else {
  326. this.room_record[this.selected.id] = [];
  327. this.room_record[this.selected.id].push(msgInfo);
  328. this.room_record_list = this.room_record[this.selected.id];
  329. }
  330. addCookie("room_record", JSON.stringify(this.room_record));
  331. }
  332. this.$nextTick(() => {
  333. // 滚动到底部
  334. this.$refs["myScrollbar"].wrap.scrollTop = this.$refs[
  335. "myScrollbar"
  336. ].wrap.scrollHeight;
  337. });
  338. },
  339. closeScoket() {
  340. // 断开连接
  341. this.$socket.close();
  342. }
  343. },
  344. created() {
  345. const self_str = localStorage.getItem("self");
  346. const room_record_str = getCookie("room_record");
  347. const friend_record_str = getCookie("friend_record");
  348. if (self_str) {
  349. this.self = JSON.parse(self_str);
  350. }
  351. if (room_record_str) {
  352. this.room_record = JSON.parse(room_record_str);
  353. }
  354. if (friend_record_str) {
  355. this.friend_record = JSON.parse(friend_record_str);
  356. }
  357. },
  358. // mounted() {
  359. // if (this.friend_record[this.selected.id]) {
  360. // this.friend_record_list = this.friend_record[this.selected.id]
  361. // }
  362. // if (this.room_record[this.selected.id]) {
  363. // this.room_record_list = this.room_record[this.selected.id]
  364. // }
  365. // },
  366. sockets: {
  367. connect() {
  368. console.log("===连接成功=========");
  369. },
  370. // 发送消息
  371. toServer(data) {
  372. this.$socket.emit("toServer", data);
  373. },
  374. // 接收消息
  375. toClient(data) {
  376. console.log(`toclient data`, data);
  377. },
  378. // 断开链接回调
  379. disconnect() {
  380. console.log("disconnect:", "已经断开 socket 链接");
  381. },
  382. // 重新连接
  383. reconnect() {
  384. // 自动执行,直到链接成功
  385. this.$socket.emit("connect");
  386. },
  387. receiveMsg(data) {
  388. console.log(`receiveMsg`, data);
  389. },
  390. chat(data) {
  391. console.log(`private message=================`, data);
  392. if (this.friend_record[data.toid]) {
  393. this.friend_record[data.toid].push(data);
  394. } else {
  395. this.friend_record[data.toid] = [];
  396. this.friend_record[data.toid].push(data);
  397. }
  398. if (this.friend_record[this.selected.id]) {
  399. this.friend_record_list = this.friend_record[this.selected.id];
  400. this.$nextTick(() => {
  401. this.$refs["myScrollbar"].wrap.scrollTop = this.$refs[
  402. "myScrollbar"
  403. ].wrap.scrollHeight;
  404. });
  405. }
  406. addCookie("room_record", JSON.stringify(this.room_record));
  407. },
  408. room_msg(data) {
  409. console.log("======room msg===");
  410. console.log(`data`, data);
  411. if (this.room_record[data.roomid]) {
  412. this.room_record[data.roomid].push(data);
  413. } else {
  414. this.room_record[data.roomid] = [];
  415. this.room_record[data.roomid].push(data);
  416. }
  417. if (this.room_record[this.selected.id]) {
  418. this.room_record_list = this.room_record[this.selected.id];
  419. this.$nextTick(() => {
  420. this.$refs["myScrollbar"].wrap.scrollTop = this.$refs[
  421. "myScrollbar"
  422. ].wrap.scrollHeight;
  423. });
  424. }
  425. addCookie("room_record", JSON.stringify(this.room_record));
  426. },
  427. user_list(userlist) {
  428. console.log(`this.self`, this.self);
  429. console.log(`userlist`, userlist);
  430. },
  431. quit(data) {
  432. console.log(`quit == quit`, data);
  433. }
  434. }
  435. };
  436. </script>
  437. <style scoped>
  438. .chat-wrap {
  439. height: 90vh;
  440. border: 1px solid #ddd;
  441. border-radius: 2px;
  442. }
  443. .chat-title {
  444. padding: 20px;
  445. font-weight: 600;
  446. }
  447. .record-warp {
  448. padding: 10px;
  449. height: 63vh;
  450. background: rgb(245, 245, 245);
  451. }
  452. /* .record-list {
  453. } */
  454. .record-item {
  455. display: flex;
  456. padding: 15px 5px;
  457. margin: 5px;
  458. }
  459. .avatar {
  460. margin: 10px 5px;
  461. }
  462. .record-item > span {
  463. padding: 8px 12px;
  464. border-radius: 5px;
  465. }
  466. .nickname {
  467. padding: 4px;
  468. margin-bottom: 2px;
  469. font-size: 14px;
  470. color: gray;
  471. }
  472. .group-record-item > span {
  473. padding: 8px 12px;
  474. border-radius: 5px;
  475. background-color: rgb(199, 206, 200);
  476. }
  477. .send-record {
  478. position: relative;
  479. justify-content: flex-end;
  480. }
  481. .receive-record {
  482. width: 60%;
  483. justify-content: flex-start;
  484. }
  485. .send-record > span {
  486. display: inline-block;
  487. text-align: right;
  488. }
  489. .message-content-text,
  490. .message-content-image {
  491. max-width: 600px;
  492. display: inline-block;
  493. margin-left: 35px;
  494. overflow: hidden;
  495. margin-top: 4px;
  496. padding: 6px;
  497. background: rgb(117, 197, 128);
  498. font-size: 16px;
  499. border-radius: 5px;
  500. text-align: left;
  501. word-break: break-word;
  502. }
  503. .message-content-image {
  504. max-height: 255px;
  505. max-width: 255px;
  506. background: rgb(204, 197, 197);
  507. }
  508. .message-content-image img {
  509. cursor: pointer;
  510. max-width: 225px;
  511. max-height: 225px;
  512. }
  513. .receive-record > span {
  514. text-align: left;
  515. background: rgb(199, 206, 200);
  516. }
  517. .input-box {
  518. overflow: hidden;
  519. }
  520. .input-box > textarea {
  521. /* display: block; */
  522. padding: 5px 0;
  523. resize: none;
  524. margin-left: 10px;
  525. width: 98%;
  526. height: 50px;
  527. outline: none;
  528. border: none;
  529. font-size: 17px;
  530. }
  531. .send-footer {
  532. padding: 10px;
  533. text-align: right;
  534. }
  535. </style>