index.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. <script>
  2. export default {
  3. name: "index",
  4. };
  5. </script>
  6. <script setup>
  7. import { ref, reactive, toRaw, toRefs, nextTick, computed, watchEffect } from "vue";
  8. import { useRouter, useRoute } from "vue-router";
  9. import useUserStore from "@/store/modules/user";
  10. import word from "@/assets/images/word2.png";
  11. import chat from "@/assets/images/profile.png";
  12. import cebian from "@/assets/images/cebian.png";
  13. import send from "@/assets/images/send.png";
  14. import downFile from "@/assets/images/down-file.png";
  15. import forwardFile from "@/assets/images/forward-file.png";
  16. import sendFile from "@/assets/images/send-file.png";
  17. import { parseTime } from "@/utils/ruoyi";
  18. import store from "@/store";
  19. import {
  20. msgFriend,
  21. userTree,
  22. userInfo,
  23. msgSend,
  24. msgRecord,
  25. delMsg,
  26. dirTree,
  27. fileTree,
  28. spaceInfo,
  29. fileDownload,
  30. } from "@/api/chat/msg";
  31. import Addperson from "@/components/AddPerson/index.vue"; //添加人员的弹框
  32. import FileTreeChoice from "@/components/FileTreeChoice/index.vue"; //选择文件发送的列表
  33. import forwordTree from "@/components/forwordTree/index.vue"; //选择文件发送的列表
  34. //websocket连接====
  35. import useWebsoctStore from "@/store/modules/websocket";
  36. import { ElMessage } from "element-plus";
  37. const route = useRoute();
  38. const websoctStore = useWebsoctStore();
  39. //====
  40. const { proxy } = getCurrentInstance();
  41. const userIds = useUserStore();
  42. const height = ref(document.documentElement.clientHeight - 74 + "px;");
  43. const messageText = ref(""); //发送的内容
  44. const headerName = ref("");
  45. const total = ref(0);
  46. const sendId = ref(""); //选择发送的文件id
  47. const isForward = ref(false);
  48. const openFile = ref(false); //文件目录
  49. const openForwardFile = ref(false); //转发目录
  50. const showCircel = ref(""); //是否 显示圆点
  51. const chatRecords = reactive({ data: [] });
  52. const loading = ref(false);
  53. const wangzhi=import.meta.env.VITE_APP_BASE_API
  54. const sendCont = reactive({
  55. //发送聊天内容数据组装
  56. data: {
  57. content: "",
  58. fileList: [],
  59. toId: 0,
  60. msgType: "",
  61. },
  62. });
  63. const searchText = ref(""); //搜
  64. const searchData = ref([]);
  65. //聊天列表数据模拟
  66. const personList = reactive({ data: [] });
  67. //获取好友列表
  68. const getMsgList = async () => {
  69. const resFriend = await msgFriend();
  70. personList.data = resFriend.rows;
  71. // .filter(
  72. // (item) => item.toId !== useUserStore().uid
  73. // );
  74. searchData.value = personList.data;
  75. //圆点======
  76. searchData.value.map((i) => {
  77. if (i.fromId == websoctStore.messOne?.fromId) {
  78. i.showCircel = true;
  79. }
  80. });
  81. //圆点======
  82. sendCont.data.toId =
  83. personList.data[0]?.toId == userIds.uid
  84. ? personList.data[0].fromId
  85. : personList.data[0].toId;
  86. headerName.value =
  87. personList.data[0]?.toId == userIds.uid
  88. ? personList.data[0].fromName
  89. : personList.data[0].toName;
  90. if (sendCont.data.toId) {
  91. //调用聊天记录
  92. msgRecordEvent(sendCont.data.toId);
  93. }
  94. };
  95. const noMes = ref(false);
  96. const boottmScroll = () => {
  97. //发送消息后滚动到最底部显示最新消息
  98. const chatContainer = document.querySelector(".right-container");
  99. // 计算滚动的目标位置
  100. const targetScrollTop = chatContainer.scrollHeight;
  101. // 设置滚动位置
  102. nextTick(() => {
  103. chatContainer.scrollTop = targetScrollTop;
  104. });
  105. };
  106. //获取用户的聊天记录
  107. const msgRecordEvent = async (toIdValue) => {
  108. const queryParams = {
  109. pageNum: 1,
  110. pageSize: 10,
  111. };
  112. const resMsgData = await msgRecord(toIdValue, queryParams);
  113. resMsgData.rows.map((i) => (i.isForward = false));
  114. chatRecords.data = resMsgData.rows.reverse();
  115. const PageNum = Math.ceil(chatRecords.data.length / 10) + 1;
  116. if (PageNum * 10 >= resMsgData.total && !noMes.value) {
  117. loading.value = false;
  118. noMes.value = true;
  119. }
  120. const nowtime = parseTime(new Date().getTime(), "{y}-{m}-{d}");
  121. chatRecords.data.map((i) => {
  122. if (nowtime == i.createTime.substr(0, 10))
  123. i.createTime = i.createTime.substring(11);
  124. });
  125. total.value = resMsgData.total;
  126. boottmScroll();
  127. };
  128. //点击左侧新建聊天
  129. const open = ref(false);
  130. const userTreeData = reactive({ data: [] });
  131. const clickNewPerson = async () => {
  132. const res = await userTree();
  133. userTreeData.data = res;
  134. toRaw(userTreeData.data);
  135. open.value = true;
  136. };
  137. //点击左侧聊天列表
  138. const clickPersonIndex = ref("");
  139. const clickPerson = (index, item) => {
  140. sendCont.data.toId = item.userId
  141. ? item.userId
  142. : item.toId == userIds.uid
  143. ? item.fromId
  144. : item.toId;
  145. headerName.value = item.nickName
  146. ? item.nickName
  147. : item.toId == userIds.uid
  148. ? item.fromName
  149. : item.toName;
  150. noMes.value = false;
  151. clickPersonIndex.value = index;
  152. //点击某个人就不显示红点
  153. searchData.value.map((i) => {
  154. if (i.fromId == item.fromId) {
  155. i.showCircel = false;
  156. }
  157. });
  158. const chatContainer = document.querySelector(".right-container");
  159. // 计算滚动的目标位置
  160. const targetScrollTop = chatContainer.scrollHeight;
  161. // 设置滚动位置
  162. chatContainer.scrollTop = targetScrollTop;
  163. msgRecordEvent(sendCont.data.toId);
  164. };
  165. //删除聊天
  166. const delClick = (msgId) => {
  167. proxy.$modal
  168. .confirm("删除后,将清空该聊天的消息记录")
  169. .then(function () {
  170. return delMsg(msgId);
  171. })
  172. .then(() => {
  173. getMsgList();
  174. proxy.$modal.msgSuccess("删除成功");
  175. });
  176. };
  177. //树选中的人传过来的选中人的信息,push进入列表中
  178. const changeMsg = async (val) => {
  179. const resInfo = await userInfo(val.id);
  180. if (searchData.value.length > 0) {
  181. //判断是否有相同的聊天人
  182. const filerData = searchData.value.filter(
  183. (vPerson) =>
  184. (vPerson.toId && vPerson.toId == resInfo.userId) ||
  185. (vPerson.userId && vPerson.userId == resInfo.userId)
  186. );
  187. if (filerData.length > 0) {
  188. return ElMessage({ message: "该聊天已存在", type: "error" });
  189. } else {
  190. searchData.value.unshift(resInfo);
  191. }
  192. } else {
  193. //为空数组的时候无需判断
  194. searchData.value.unshift(resInfo);
  195. }
  196. sendCont.data.toId = resInfo.userId;
  197. msgRecordEvent(sendCont.data.toId);
  198. headerName.value = resInfo.nickName;
  199. };
  200. //发送聊天
  201. const inputRef = ref(null);
  202. const msgSendClick = (event) => {
  203. if (event.ctrlKey && event.keyCode == 13) {
  204. //CTRL+enter键换行
  205. const textarea = event.target;
  206. const start = textarea.selectionStart;
  207. const end = textarea.selectionEnd;
  208. const text = messageText.value;
  209. messageText.value = text.slice(0, start) + "\n" + text.slice(end);
  210. nextTick(() => {
  211. textarea.selectionStart = start + 1;
  212. textarea.selectionEnd = start + 1;
  213. });
  214. event.preventDefault();
  215. } else if (event.shiftKey && event.keyCode == 13) {
  216. event.preventDefault();
  217. } else {
  218. noMes.value = false;
  219. if (messageText.value.trim() == "") {
  220. return ElMessage({ message: "不能发送空白消息", type: "error" });
  221. }
  222. const message = {
  223. content: messageText.value,
  224. msgType: "2",
  225. fileList: [],
  226. toId: sendCont.data.toId,
  227. };
  228. websoctStore.sendMessage(message);
  229. boottmScroll();
  230. event.preventDefault();
  231. messageText.value = "";
  232. // 将光标设置到输入框第一行
  233. }
  234. };
  235. //发送文件确认按钮
  236. const fileChangeMsg = async (val) => {
  237. const message = {
  238. content: val.id,
  239. msgType: "1",
  240. fileList: [],
  241. toId: sendCont.data.toId,
  242. };
  243. sendId.value = val.id;
  244. websoctStore.sendMessage(message);
  245. };
  246. //点击发送文件图标
  247. const fileUserTreeData = reactive({ data: {} });
  248. const sendFileClick = async () => {
  249. const resDir = await fileTree(3);
  250. fileUserTreeData.data = resDir;
  251. toRaw(fileUserTreeData.data);
  252. openFile.value = true;
  253. };
  254. //获取用户聊天记录
  255. const handleNewMessage = async () => {
  256. if (websoctStore.newMessage) {
  257. msgRecordEvent(sendCont.data.toId); //获取用户的聊天记录
  258. const resFriend = await msgFriend();
  259. getMsgList();
  260. websoctStore.newMessage = false; // 重置新消息标记
  261. }
  262. };
  263. watchEffect(() => {
  264. if (websoctStore.messOne?.fromId) {
  265. msgRecordEvent(websoctStore.messOne.fromId);
  266. getMsgList();
  267. }
  268. });
  269. // 滚动翻页========
  270. const mainContainer = ref(null);
  271. const handleScroll = (event) => {
  272. // 在滚动到顶部时,加载上一页的聊天记录
  273. const mainContainer2 = document.querySelector(".right-container");
  274. if (
  275. event.deltaY < 0 &&
  276. mainContainer2.scrollTop <= 1 &&
  277. !noMes.value &&
  278. !loading.value
  279. ) {
  280. loading.value = true;
  281. loadPreviousPage();
  282. }
  283. };
  284. const loadPreviousPage = async () => {
  285. const currentPageNum = Math.ceil(chatRecords.data.length / 10) + 1;
  286. const queryParams = {
  287. pageNum: currentPageNum,
  288. pageSize: 10,
  289. };
  290. // setTimeout(async () => {
  291. const resMsgData = await msgRecord(sendCont.data.toId, queryParams);
  292. const previousPageData = resMsgData.rows.reverse();
  293. chatRecords.data = [...previousPageData, ...chatRecords.data];
  294. const nowtime2 = parseTime(new Date().getTime(), "{y}-{m}-{d}");
  295. chatRecords.data.map((i) => {
  296. if (nowtime2 == i.createTime.substr(0, 10))
  297. i.createTime = i.createTime.substring(11);
  298. });
  299. noMes.value = false;
  300. if (currentPageNum * 10 >= resMsgData.total && !noMes.value) {
  301. loading.value = false;
  302. noMes.value = true;
  303. return;
  304. }
  305. //获取数据后滚动到获得新消息的最后一条,而不是第一条=========
  306. await nextTick();
  307. const mainContainer = document.querySelector(".right-container");
  308. const newMessages = document.querySelectorAll(".message-container");
  309. const firstNewMessage = newMessages[previousPageData.length];
  310. const firstNewMessageTop = mainContainer.offsetHeight + 100;
  311. mainContainer.scrollTop = firstNewMessageTop;
  312. chatRecords.data;
  313. //===========
  314. // 将加载的上一页聊天记录插入到 chatRecords.data 的前面
  315. loading.value = false;
  316. // }, 50);
  317. };
  318. const transferFiles = (forwardVal, msgIds, indexs) => {
  319. chatRecords.data.map((i, index) => {
  320. if (i.msgId == msgIds && i.msgType == "1" && indexs == index) {
  321. i.isForward = true;
  322. } else {
  323. i.isForward = false;
  324. }
  325. });
  326. };
  327. // 点击转存
  328. const forwardTreeData = reactive({ data: {} });
  329. const spaceId = ref("");
  330. const docId = ref("");
  331. const forwardClick = async (indexs, docIds) => {
  332. docId.value = docIds ? docIds.toString() : "";
  333. const resDir = await dirTree(3);
  334. forwardTreeData.data = resDir;
  335. toRaw(forwardTreeData.data);
  336. //获取最上层树的id
  337. const topSpaceid = await spaceInfo(3);
  338. spaceId.value = topSpaceid.data.spaceId.toString();
  339. openForwardFile.value = true;
  340. };
  341. const forwardChangeMsg = async (val) => {};
  342. //点击下载
  343. const downClick = async (fileId) => {
  344. location.href = `${import.meta.env.VITE_APP_BASE_API}/api/download/${fileId}`;
  345. };
  346. //搜索的点击事件
  347. const SearchChat = () => {
  348. if (searchText.value) {
  349. searchData.value = personList.data.filter((i) => {
  350. return i.toName == searchText.value;
  351. });
  352. } else {
  353. getMsgList();
  354. }
  355. };
  356. //转聊天记录的/n为实际样式
  357. const formatText = (text) => {
  358. const formattedText = text.replace(/\n/g, "<br>");
  359. return formattedText;
  360. };
  361. // 滚动翻页========
  362. onMounted(() => {
  363. getMsgList();
  364. websoctStore.connect();
  365. setInterval(handleNewMessage, 1000); // 每秒钟检查是否有新消息
  366. });
  367. </script>
  368. <template>
  369. <div class="main" :style="'height:' + height">
  370. <!-- 左侧用户列表 -->
  371. <div class="left-main">
  372. <div class="left-top search">
  373. <el-input
  374. v-model="searchText"
  375. maxlength="32"
  376. class="w-50 m-2"
  377. size="small"
  378. clearable
  379. placeholder="搜索聊天"
  380. suffix-icon="Search"
  381. @change="SearchChat"
  382. />
  383. <!-- 添加聊天人员 -->
  384. <el-icon
  385. size="24"
  386. color="#505870"
  387. @click="clickNewPerson"
  388. style="margin-right: 8px"
  389. ><Plus
  390. /></el-icon>
  391. <!-- 新建聊天弹框 -->
  392. <Addperson
  393. :open="open"
  394. @close="open = false"
  395. :userTreeData="userTreeData.data"
  396. @changeMsg="changeMsg"
  397. ></Addperson>
  398. </div>
  399. <!-- 列表 -->
  400. <div
  401. :class="
  402. clickPersonIndex == index
  403. ? 'activ-left-container left-container'
  404. : 'left-container'
  405. "
  406. v-for="(item, index) in searchData"
  407. :key="index"
  408. @click="clickPerson(index, item)"
  409. >
  410. <img :src="cebian" class="cebian" v-if="clickPersonIndex == index" />
  411. <!-- <button
  412. class="del-chat"
  413. v-if="clickPersonIndex == index"
  414. @click="delClick(item.msgId)"
  415. >
  416. 删除聊天
  417. </button> -->
  418. <div>
  419. <!-- <img :src="item.avatar?item.avatar : chat" class="head-sculpture" /> -->
  420. <img
  421. :src="wangzhi + item.fromAvatar"
  422. class="head-sculpture"
  423. v-if="item.toId == userIds.uid && item.fromAvatar"
  424. />
  425. <img :src="item.avatar " alt="" v-else-if="item.avatar"/>
  426. <img
  427. :src="wangzhi + item.toAvatar"
  428. class="head-sculpture"
  429. v-else-if="item.toId != userIds.uid && item.toAvatar"
  430. />
  431. <span
  432. style="
  433. background-color: #7a89ba;
  434. display: inline-block;
  435. color: #fff;
  436. font-weight: 600;
  437. text-align: center;
  438. line-height: 40px;
  439. "
  440. class="head-sculpture"
  441. v-else
  442. >{{ item.nickName
  443. ? item.nickName?.slice(0, 1):item.toId == userIds.uid? item.fromName?.slice(0, 1): item.toName?.slice(0, 1) }}</span>
  444. </div>
  445. <div class="spill">
  446. <span class="person-name">{{
  447. item.nickName
  448. ? item.nickName
  449. : item.toId == userIds.uid
  450. ? item.fromName
  451. : item.toName
  452. }}</span
  453. ><span class="person-cont spill">
  454. {{ item.file?.fileName ? item.file?.fileName : item.content }}</span
  455. >
  456. </div>
  457. <span
  458. class="yuandian"
  459. v-if="item.showCircel && $route.path == '/index'"
  460. ></span>
  461. </div>
  462. </div>
  463. <!-- 右侧聊天 -->
  464. <div class="right-main">
  465. <div
  466. class="common-layout"
  467. style="display: flex; flex-direction: column; height: 100vh"
  468. >
  469. <el-container>
  470. <el-header height="64px" class="right-header flex-buju">{{
  471. headerName
  472. }}</el-header>
  473. <!-- 聊天 -->
  474. <el-main
  475. class="right-container"
  476. @mousewheel="handleScroll"
  477. ref="mainContainer"
  478. >
  479. <div v-loading="loading"></div>
  480. <!-- <div v-if="noMes" style="color:#9fa1a5 ;">无更多聊天记录</div> -->
  481. <div
  482. class="message-container"
  483. v-for="(record, index) in chatRecords.data"
  484. :class="{
  485. 'message-left': useUserStore().uid == record.toId,
  486. 'message-right': useUserStore().uid !== record.toId,
  487. }"
  488. :key="index"
  489. >
  490. <div>
  491. <div
  492. v-if="useUserStore().uid !== record.toId"
  493. style="display: flex"
  494. >
  495. <div
  496. :class="
  497. record.msgType == '1'
  498. ? 'file-msg right-back'
  499. : 'time-text right-back'
  500. "
  501. >
  502. <img
  503. :src="word"
  504. v-if="record.msgType == '1'"
  505. class="head-sculpture"
  506. />
  507. <div
  508. :class="
  509. record.msgType == '2' ? 'clip-path' : 'clip-path-right'
  510. "
  511. >
  512. <div
  513. style="
  514. color: #c1cce3;
  515. font-size: 12px;
  516. margin-bottom: 4px;
  517. "
  518. >
  519. {{ record.createTime.slice(0, -3) }}
  520. </div>
  521. <span v-if="record.msgType == '1'">{{
  522. record.file?.fileName
  523. }}</span>
  524. <!-- <span v-else-if="record.msgType == '2'">{{
  525. record.content
  526. }}</span> -->
  527. <span
  528. v-else-if="record.msgType == '2'"
  529. v-html="formatText(record.content)"
  530. ></span>
  531. </div>
  532. </div>
  533. <div>
  534. <img
  535. :src="wangzhi + record.fromAvatar"
  536. class="head-sculpture"
  537. v-if="
  538. useUserStore().uid != record.toId && record.fromAvatar
  539. "
  540. />
  541. <img
  542. :src="wangzhi + record.toAvatar"
  543. class="head-sculpture"
  544. v-else-if="
  545. useUserStore().uid == record.toId && record.toAvatar
  546. "
  547. />
  548. <span
  549. style="
  550. background-color: #7a89ba;
  551. display: inline-block;
  552. color: #fff;
  553. font-weight: 600;
  554. text-align: center;
  555. line-height: 40px;
  556. "
  557. class="head-sculpture"
  558. v-else
  559. >{{ record.fromName.slice(0, 1) }}</span
  560. >
  561. </div>
  562. </div>
  563. <!-- 头像 -->
  564. <!-- <img :src="chat" class="head-sculpture" /> -->
  565. </div>
  566. <div style="display: flex; align-items: center">
  567. <div
  568. v-if="useUserStore().uid == record.toId"
  569. style="display: flex"
  570. >
  571. <div>
  572. <img
  573. :src="wangzhi + record.fromAvatar"
  574. class="head-sculpture"
  575. v-if="
  576. useUserStore().uid == record.toId && record.fromAvatar
  577. "
  578. />
  579. <img
  580. :src="wangzhi + record.toAvatar"
  581. class="head-sculpture"
  582. v-else-if="
  583. useUserStore().uid != record.toId && record.toAvatar
  584. "
  585. />
  586. <span
  587. style="
  588. background-color: #7a89ba;
  589. display: inline-block;
  590. color: #fff;
  591. font-weight: 600;
  592. text-align: center;
  593. line-height: 40px;
  594. "
  595. class="head-sculpture"
  596. v-else
  597. >{{ record.fromName.slice(0, 1) }}</span
  598. >
  599. </div>
  600. <div
  601. :class="
  602. record.msgType == '1' ? 'file-msg left-back' : 'left-back'
  603. "
  604. @click="transferFiles(record.msgType, record.msgId, index)"
  605. >
  606. <img
  607. v-if="record.msgType == '1'"
  608. :src="word"
  609. class="head-sculpture"
  610. />
  611. <div
  612. :class="
  613. record.msgType == '2' ? 'clip-path' : 'clip-path-left'
  614. "
  615. >
  616. <div
  617. style="
  618. color: #7a89ba;
  619. font-size: 12px;
  620. margin-bottom: 4px;
  621. "
  622. >
  623. {{ record.createTime.slice(0, -3) }}
  624. </div>
  625. <span v-if="record.msgType == '1'">{{
  626. record.file?.fileName
  627. }}</span>
  628. <span
  629. v-else-if="record.msgType == '2'"
  630. v-html="formatText(record.content)"
  631. ></span>
  632. </div>
  633. </div>
  634. </div>
  635. <img
  636. :src="forwardFile"
  637. class="zhuanfa forwd"
  638. alt="转存"
  639. v-if="record.isForward"
  640. @click="forwardClick(index, record.file?.docId)"
  641. />
  642. <img
  643. :src="downFile"
  644. class="zhuanfa downf"
  645. alt="下载"
  646. v-if="record.isForward"
  647. @click="downClick(record.file.fileId)"
  648. />
  649. </div>
  650. </div>
  651. </el-main>
  652. <!-- 底部 -->
  653. <el-footer height="112px" class="right-footer">
  654. <!-- 发送文件 -->
  655. <img
  656. :src="sendFile"
  657. class="send-info-file"
  658. @click="sendFileClick"
  659. />
  660. <FileTreeChoice
  661. :openFile="openFile"
  662. @close="openFile = false"
  663. :fileUserTreeData="fileUserTreeData.data"
  664. @fileChangeMsg="fileChangeMsg"
  665. ></FileTreeChoice>
  666. <el-input
  667. v-model="messageText"
  668. class="w-50 m-2"
  669. type="textarea"
  670. :autosize="{ minRows: 3, maxRows: 5 }"
  671. clearable
  672. ref="inputRef"
  673. size="small"
  674. placeholder="请输入聊天内容"
  675. maxlength="800"
  676. @keyup.ctrl.enter="msgSendClick($event)"
  677. @keyup.shift.enter="msgSendClick($event)"
  678. @keyup.enter="msgSendClick($event)"
  679. />
  680. <!-- 发送按钮 -->
  681. <img :src="send" class="send-info" @click="msgSendClick" />
  682. </el-footer>
  683. </el-container>
  684. </div>
  685. </div>
  686. </div>
  687. <!-- 发送消息按钮选择的文件 -->
  688. <forwordTree
  689. :openForwardFile="openForwardFile"
  690. :docId="docId"
  691. :spaceId="spaceId"
  692. @close="openForwardFile = false"
  693. :forwardTreeData="forwardTreeData.data"
  694. @forwardChangeMsg="forwardChangeMsg"
  695. ></forwordTree>
  696. </template>
  697. <style lang="scss" scoped>
  698. @import "@/assets/styles/my-common.scss";
  699. .left-container {
  700. position: relative;
  701. }
  702. .yuandian {
  703. width: 8px;
  704. height: 8px;
  705. position: absolute;
  706. right: 8px;
  707. top: 6px;
  708. background: #fa5151;
  709. border-radius: 4px;
  710. }
  711. :deep(.el-textarea__inner) {
  712. resize: none;
  713. }
  714. </style>