123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736 |
- <script>
- export default {
- name: "index",
- };
- </script>
- <script setup>
- import { ref, reactive, toRaw, toRefs, nextTick, computed, watchEffect } from "vue";
- import { useRouter, useRoute } from "vue-router";
- import useUserStore from "@/store/modules/user";
- import word from "@/assets/images/word2.png";
- import chat from "@/assets/images/profile.png";
- import cebian from "@/assets/images/cebian.png";
- import send from "@/assets/images/send.png";
- import downFile from "@/assets/images/down-file.png";
- import forwardFile from "@/assets/images/forward-file.png";
- import sendFile from "@/assets/images/send-file.png";
- import { parseTime } from "@/utils/ruoyi";
- import store from "@/store";
- import {
- msgFriend,
- userTree,
- userInfo,
- msgSend,
- msgRecord,
- delMsg,
- dirTree,
- fileTree,
- spaceInfo,
- fileDownload,
- } from "@/api/chat/msg";
- import Addperson from "@/components/AddPerson/index.vue"; //添加人员的弹框
- import FileTreeChoice from "@/components/FileTreeChoice/index.vue"; //选择文件发送的列表
- import forwordTree from "@/components/forwordTree/index.vue"; //选择文件发送的列表
- //websocket连接====
- import useWebsoctStore from "@/store/modules/websocket";
- import { ElMessage } from "element-plus";
- const route = useRoute();
- const websoctStore = useWebsoctStore();
- //====
- const { proxy } = getCurrentInstance();
- const userIds = useUserStore();
- const height = ref(document.documentElement.clientHeight - 74 + "px;");
- const messageText = ref(""); //发送的内容
- const headerName = ref("");
- const total = ref(0);
- const sendId = ref(""); //选择发送的文件id
- const isForward = ref(false);
- const openFile = ref(false); //文件目录
- const openForwardFile = ref(false); //转发目录
- const showCircel = ref(""); //是否 显示圆点
- const chatRecords = reactive({ data: [] });
- const loading = ref(false);
- const wangzhi=import.meta.env.VITE_APP_BASE_API
- const sendCont = reactive({
- //发送聊天内容数据组装
- data: {
- content: "",
- fileList: [],
- toId: 0,
- msgType: "",
- },
- });
- const searchText = ref(""); //搜
- const searchData = ref([]);
- //聊天列表数据模拟
- const personList = reactive({ data: [] });
- //获取好友列表
- const getMsgList = async () => {
- const resFriend = await msgFriend();
- personList.data = resFriend.rows;
- // .filter(
- // (item) => item.toId !== useUserStore().uid
- // );
- searchData.value = personList.data;
- //圆点======
- searchData.value.map((i) => {
- if (i.fromId == websoctStore.messOne?.fromId) {
- i.showCircel = true;
- }
- });
- //圆点======
- sendCont.data.toId =
- personList.data[0]?.toId == userIds.uid
- ? personList.data[0].fromId
- : personList.data[0].toId;
- headerName.value =
- personList.data[0]?.toId == userIds.uid
- ? personList.data[0].fromName
- : personList.data[0].toName;
- if (sendCont.data.toId) {
- //调用聊天记录
- msgRecordEvent(sendCont.data.toId);
- }
- };
- const noMes = ref(false);
- const boottmScroll = () => {
- //发送消息后滚动到最底部显示最新消息
- const chatContainer = document.querySelector(".right-container");
- // 计算滚动的目标位置
- const targetScrollTop = chatContainer.scrollHeight;
- // 设置滚动位置
- nextTick(() => {
- chatContainer.scrollTop = targetScrollTop;
- });
- };
- //获取用户的聊天记录
- const msgRecordEvent = async (toIdValue) => {
- const queryParams = {
- pageNum: 1,
- pageSize: 10,
- };
- const resMsgData = await msgRecord(toIdValue, queryParams);
- resMsgData.rows.map((i) => (i.isForward = false));
- chatRecords.data = resMsgData.rows.reverse();
- const PageNum = Math.ceil(chatRecords.data.length / 10) + 1;
- if (PageNum * 10 >= resMsgData.total && !noMes.value) {
- loading.value = false;
- noMes.value = true;
- }
- const nowtime = parseTime(new Date().getTime(), "{y}-{m}-{d}");
- chatRecords.data.map((i) => {
- if (nowtime == i.createTime.substr(0, 10))
- i.createTime = i.createTime.substring(11);
- });
- total.value = resMsgData.total;
- boottmScroll();
- };
- //点击左侧新建聊天
- const open = ref(false);
- const userTreeData = reactive({ data: [] });
- const clickNewPerson = async () => {
- const res = await userTree();
- userTreeData.data = res;
- toRaw(userTreeData.data);
- open.value = true;
- };
- //点击左侧聊天列表
- const clickPersonIndex = ref("");
- const clickPerson = (index, item) => {
- sendCont.data.toId = item.userId
- ? item.userId
- : item.toId == userIds.uid
- ? item.fromId
- : item.toId;
- headerName.value = item.nickName
- ? item.nickName
- : item.toId == userIds.uid
- ? item.fromName
- : item.toName;
- noMes.value = false;
- clickPersonIndex.value = index;
- //点击某个人就不显示红点
- searchData.value.map((i) => {
- if (i.fromId == item.fromId) {
- i.showCircel = false;
- }
- });
- const chatContainer = document.querySelector(".right-container");
- // 计算滚动的目标位置
- const targetScrollTop = chatContainer.scrollHeight;
- // 设置滚动位置
- chatContainer.scrollTop = targetScrollTop;
- msgRecordEvent(sendCont.data.toId);
- };
- //删除聊天
- const delClick = (msgId) => {
- proxy.$modal
- .confirm("删除后,将清空该聊天的消息记录")
- .then(function () {
- return delMsg(msgId);
- })
- .then(() => {
- getMsgList();
- proxy.$modal.msgSuccess("删除成功");
- });
- };
- //树选中的人传过来的选中人的信息,push进入列表中
- const changeMsg = async (val) => {
- const resInfo = await userInfo(val.id);
- if (searchData.value.length > 0) {
- //判断是否有相同的聊天人
- const filerData = searchData.value.filter(
- (vPerson) =>
- (vPerson.toId && vPerson.toId == resInfo.userId) ||
- (vPerson.userId && vPerson.userId == resInfo.userId)
- );
- if (filerData.length > 0) {
- return ElMessage({ message: "该聊天已存在", type: "error" });
- } else {
- searchData.value.unshift(resInfo);
- }
- } else {
- //为空数组的时候无需判断
- searchData.value.unshift(resInfo);
- }
- sendCont.data.toId = resInfo.userId;
- msgRecordEvent(sendCont.data.toId);
- headerName.value = resInfo.nickName;
- };
- //发送聊天
- const inputRef = ref(null);
- const msgSendClick = (event) => {
- if (event.ctrlKey && event.keyCode == 13) {
- //CTRL+enter键换行
- const textarea = event.target;
- const start = textarea.selectionStart;
- const end = textarea.selectionEnd;
- const text = messageText.value;
- messageText.value = text.slice(0, start) + "\n" + text.slice(end);
- nextTick(() => {
- textarea.selectionStart = start + 1;
- textarea.selectionEnd = start + 1;
- });
- event.preventDefault();
- } else if (event.shiftKey && event.keyCode == 13) {
- event.preventDefault();
- } else {
- noMes.value = false;
- if (messageText.value.trim() == "") {
- return ElMessage({ message: "不能发送空白消息", type: "error" });
- }
- const message = {
- content: messageText.value,
- msgType: "2",
- fileList: [],
- toId: sendCont.data.toId,
- };
- websoctStore.sendMessage(message);
- boottmScroll();
- event.preventDefault();
- messageText.value = "";
- // 将光标设置到输入框第一行
- }
- };
- //发送文件确认按钮
- const fileChangeMsg = async (val) => {
- const message = {
- content: val.id,
- msgType: "1",
- fileList: [],
- toId: sendCont.data.toId,
- };
- sendId.value = val.id;
- websoctStore.sendMessage(message);
- };
- const getNewMsgList=async ()=>{
- const resFriend = await msgFriend();
- personList.data = resFriend.rows;
- //圆点======
- searchData.value.map((i) => {
- personList.data.map((v)=>{
- if (v.fromId == websoctStore.messOne?.fromId) {
- v.showCircel = true;
- }
- if(i.msgId==v.msgId){
- i=v
- }
- })
- });
- console.log('searchData.value', searchData.value)
- }
- //点击发送文件图标
- const fileUserTreeData = reactive({ data: {} });
- const sendFileClick = async () => {
- const resDir = await fileTree(3);
- fileUserTreeData.data = resDir;
- toRaw(fileUserTreeData.data);
- openFile.value = true;
- };
- //获取用户聊天记录
- const handleNewMessage = async () => {
- if (websoctStore.newMessage) {
- msgRecordEvent(sendCont.data.toId); //获取用户的聊天记录
- getNewMsgList();
- websoctStore.newMessage = false; // 重置新消息标记
- }
- };
- watchEffect(() => {
- if (websoctStore.messOne?.fromId) {
- msgRecordEvent(websoctStore.messOne.fromId);
- getNewMsgList();
- }
- });
- // 滚动翻页========
- const mainContainer = ref(null);
- const handleScroll = (event) => {
- // 在滚动到顶部时,加载上一页的聊天记录
- const mainContainer2 = document.querySelector(".right-container");
- if (
- event.deltaY < 0 &&
- mainContainer2.scrollTop <= 1 &&
- !noMes.value &&
- !loading.value
- ) {
- loading.value = true;
- loadPreviousPage();
- }
- };
- const loadPreviousPage = async () => {
- const currentPageNum = Math.ceil(chatRecords.data.length / 10) + 1;
- const queryParams = {
- pageNum: currentPageNum,
- pageSize: 10,
- };
- // setTimeout(async () => {
- const resMsgData = await msgRecord(sendCont.data.toId, queryParams);
- const previousPageData = resMsgData.rows.reverse();
- chatRecords.data = [...previousPageData, ...chatRecords.data];
- const nowtime2 = parseTime(new Date().getTime(), "{y}-{m}-{d}");
- chatRecords.data.map((i) => {
- if (nowtime2 == i.createTime.substr(0, 10))
- i.createTime = i.createTime.substring(11);
- });
- noMes.value = false;
- if (currentPageNum * 10 >= resMsgData.total && !noMes.value) {
- loading.value = false;
- noMes.value = true;
- return;
- }
- //获取数据后滚动到获得新消息的最后一条,而不是第一条=========
- await nextTick();
- const mainContainer = document.querySelector(".right-container");
- const newMessages = document.querySelectorAll(".message-container");
- const firstNewMessage = newMessages[previousPageData.length];
- const firstNewMessageTop = mainContainer.offsetHeight + 100;
- mainContainer.scrollTop = firstNewMessageTop;
- chatRecords.data;
- //===========
- // 将加载的上一页聊天记录插入到 chatRecords.data 的前面
- loading.value = false;
- // }, 50);
- };
- const transferFiles = (forwardVal, msgIds, indexs) => {
- chatRecords.data.map((i, index) => {
- if (i.msgId == msgIds && i.msgType == "1" && indexs == index) {
- i.isForward = true;
- } else {
- i.isForward = false;
- }
- });
- };
- // 点击转存
- const forwardTreeData = reactive({ data: {} });
- const spaceId = ref("");
- const docId = ref("");
- const forwardClick = async (indexs, docIds) => {
- docId.value = docIds ? docIds.toString() : "";
- const resDir = await dirTree(3);
- forwardTreeData.data = resDir;
- toRaw(forwardTreeData.data);
- //获取最上层树的id
- const topSpaceid = await spaceInfo(3);
- spaceId.value = topSpaceid.data.spaceId.toString();
- openForwardFile.value = true;
- };
- const forwardChangeMsg = async (val) => {};
- //点击下载
- const downClick = async (fileId) => {
- location.href = `${import.meta.env.VITE_APP_BASE_API}/api/download/${fileId}`;
- };
- //搜索的点击事件
- const SearchChat = () => {
- if (searchText.value) {
- searchData.value = personList.data.filter((i) => {
- return i.toName == searchText.value;
- });
- } else {
- getMsgList();
- }
- };
- //转聊天记录的/n为实际样式
- const formatText = (text) => {
- const formattedText = text.replace(/\n/g, "<br>");
- return formattedText;
- };
- // 滚动翻页========
- onMounted(() => {
- getMsgList();
- websoctStore.connect();
- setInterval(handleNewMessage, 1000); // 每秒钟检查是否有新消息
- });
- </script>
- <template>
- <div class="main" :style="'height:' + height">
- <!-- 左侧用户列表 -->
- <div class="left-main">
- <div class="left-top search">
- <el-input
- v-model="searchText"
- maxlength="32"
- class="w-50 m-2"
- size="small"
- clearable
- placeholder="搜索聊天"
- suffix-icon="Search"
- @change="SearchChat"
- />
- <!-- 添加聊天人员 -->
- <el-icon
- size="24"
- color="#505870"
- @click="clickNewPerson"
- style="margin-right: 8px"
- ><Plus
- /></el-icon>
- <!-- 新建聊天弹框 -->
- <Addperson
- :open="open"
- @close="open = false"
- :userTreeData="userTreeData.data"
- @changeMsg="changeMsg"
- ></Addperson>
- </div>
- <!-- 列表 -->
- <div
- :class="
- clickPersonIndex == index
- ? 'activ-left-container left-container'
- : 'left-container'
- "
- v-for="(item, index) in searchData"
- :key="index"
- @click="clickPerson(index, item)"
- >
- <img :src="cebian" class="cebian" v-if="clickPersonIndex == index" />
- <!-- <button
- class="del-chat"
- v-if="clickPersonIndex == index"
- @click="delClick(item.msgId)"
- >
- 删除聊天
- </button> -->
- <div>
- <!-- <img :src="item.avatar?item.avatar : chat" class="head-sculpture" /> -->
- <img
- :src="wangzhi + item.fromAvatar"
- class="head-sculpture"
- v-if="item.toId == userIds.uid && item.fromAvatar"
- />
- <img :src="item.avatar " alt="" v-else-if="item.avatar"/>
- <img
- :src="wangzhi + item.toAvatar"
- class="head-sculpture"
- v-else-if="item.toId != userIds.uid && item.toAvatar"
- />
- <span
- style="
- background-color: #7a89ba;
- display: inline-block;
- color: #fff;
- font-weight: 600;
- text-align: center;
- line-height: 40px;
- "
- class="head-sculpture"
- v-else
- >{{ item.nickName
- ? item.nickName?.slice(0, 1):item.toId == userIds.uid? item.fromName?.slice(0, 1): item.toName?.slice(0, 1) }}</span>
- </div>
- <div class="spill">
- <span class="person-name">{{
- item.nickName
- ? item.nickName
- : item.toId == userIds.uid
- ? item.fromName
- : item.toName
- }}</span
- ><span class="person-cont spill">
- {{ item.file?.fileName ? item.file?.fileName : item.content }}</span
- >
- </div>
- <span
- class="yuandian"
- v-if="item.showCircel && $route.path == '/index'"
- ></span>
- </div>
- </div>
- <!-- 右侧聊天 -->
- <div class="right-main">
- <div
- class="common-layout"
- style="display: flex; flex-direction: column; height: 100vh"
- >
- <el-container>
- <el-header height="64px" class="right-header flex-buju">{{
- headerName
- }}</el-header>
- <!-- 聊天 -->
- <el-main
- class="right-container"
- @mousewheel="handleScroll"
- ref="mainContainer"
- >
- <div v-loading="loading"></div>
- <!-- <div v-if="noMes" style="color:#9fa1a5 ;">无更多聊天记录</div> -->
- <div
- class="message-container"
- v-for="(record, index) in chatRecords.data"
- :class="{
- 'message-left': useUserStore().uid == record.toId,
- 'message-right': useUserStore().uid !== record.toId,
- }"
- :key="index"
- >
- <div>
- <div
- v-if="useUserStore().uid !== record.toId"
- style="display: flex"
- >
- <div
- :class="
- record.msgType == '1'
- ? 'file-msg right-back'
- : 'time-text right-back'
- "
- >
- <img
- :src="word"
- v-if="record.msgType == '1'"
- class="head-sculpture"
- />
- <div
- :class="
- record.msgType == '2' ? 'clip-path' : 'clip-path-right'
- "
- >
- <div
- style="
- color: #c1cce3;
- font-size: 12px;
- margin-bottom: 4px;
- "
- >
- {{ record.createTime.slice(0, -3) }}
- </div>
- <span v-if="record.msgType == '1'">{{
- record.file?.fileName
- }}</span>
- <!-- <span v-else-if="record.msgType == '2'">{{
- record.content
- }}</span> -->
- <span
- v-else-if="record.msgType == '2'"
- v-html="formatText(record.content)"
- ></span>
- </div>
- </div>
- <div>
- <img
- :src="wangzhi + record.fromAvatar"
- class="head-sculpture"
- v-if="
- useUserStore().uid != record.toId && record.fromAvatar
- "
- />
- <img
- :src="wangzhi + record.toAvatar"
- class="head-sculpture"
- v-else-if="
- useUserStore().uid == record.toId && record.toAvatar
- "
- />
- <span
- style="
- background-color: #7a89ba;
- display: inline-block;
- color: #fff;
- font-weight: 600;
- text-align: center;
- line-height: 40px;
- "
- class="head-sculpture"
- v-else
- >{{ record.fromName.slice(0, 1) }}</span
- >
- </div>
- </div>
- <!-- 头像 -->
- <!-- <img :src="chat" class="head-sculpture" /> -->
- </div>
- <div style="display: flex; align-items: center">
- <div
- v-if="useUserStore().uid == record.toId"
- style="display: flex"
- >
- <div>
- <img
- :src="wangzhi + record.fromAvatar"
- class="head-sculpture"
- v-if="
- useUserStore().uid == record.toId && record.fromAvatar
- "
- />
- <img
- :src="wangzhi + record.toAvatar"
- class="head-sculpture"
- v-else-if="
- useUserStore().uid != record.toId && record.toAvatar
- "
- />
- <span
- style="
- background-color: #7a89ba;
- display: inline-block;
- color: #fff;
- font-weight: 600;
- text-align: center;
- line-height: 40px;
- "
- class="head-sculpture"
- v-else
- >{{ record.fromName.slice(0, 1) }}</span
- >
- </div>
- <div
- :class="
- record.msgType == '1' ? 'file-msg left-back' : 'left-back'
- "
- @click="transferFiles(record.msgType, record.msgId, index)"
- >
- <img
- v-if="record.msgType == '1'"
- :src="word"
- class="head-sculpture"
- />
- <div
- :class="
- record.msgType == '2' ? 'clip-path' : 'clip-path-left'
- "
- >
- <div
- style="
- color: #7a89ba;
- font-size: 12px;
- margin-bottom: 4px;
- "
- >
- {{ record.createTime.slice(0, -3) }}
- </div>
- <span v-if="record.msgType == '1'">{{
- record.file?.fileName
- }}</span>
- <span
- v-else-if="record.msgType == '2'"
- v-html="formatText(record.content)"
- ></span>
- </div>
- </div>
- </div>
- <img
- :src="forwardFile"
- class="zhuanfa forwd"
- alt="转存"
- v-if="record.isForward"
- @click="forwardClick(index, record.file?.docId)"
- />
- <img
- :src="downFile"
- class="zhuanfa downf"
- alt="下载"
- v-if="record.isForward"
- @click="downClick(record.file.fileId)"
- />
- </div>
- </div>
- </el-main>
- <!-- 底部 -->
- <el-footer height="112px" class="right-footer">
- <!-- 发送文件 -->
- <img
- :src="sendFile"
- class="send-info-file"
- @click="sendFileClick"
- />
- <FileTreeChoice
- :openFile="openFile"
- @close="openFile = false"
- :fileUserTreeData="fileUserTreeData.data"
- @fileChangeMsg="fileChangeMsg"
- ></FileTreeChoice>
- <el-input
- v-model="messageText"
- class="w-50 m-2"
- type="textarea"
- :autosize="{ minRows: 3, maxRows: 5 }"
- clearable
- ref="inputRef"
- size="small"
- placeholder="请输入聊天内容"
- maxlength="800"
- @keyup.ctrl.enter="msgSendClick($event)"
- @keyup.shift.enter="msgSendClick($event)"
- @keyup.enter="msgSendClick($event)"
- />
- <!-- 发送按钮 -->
- <img :src="send" class="send-info" @click="msgSendClick" />
- </el-footer>
- </el-container>
- </div>
- </div>
- </div>
- <!-- 发送消息按钮选择的文件 -->
- <forwordTree
- :openForwardFile="openForwardFile"
- :docId="docId"
- :spaceId="spaceId"
- @close="openForwardFile = false"
- :forwardTreeData="forwardTreeData.data"
- @forwardChangeMsg="forwardChangeMsg"
- ></forwordTree>
- </template>
- <style lang="scss" scoped>
- @import "@/assets/styles/my-common.scss";
- .left-container {
- position: relative;
- }
- .yuandian {
- width: 8px;
- height: 8px;
- position: absolute;
- right: 8px;
- top: 6px;
- background: #fa5151;
- border-radius: 4px;
- }
- :deep(.el-textarea__inner) {
- resize: none;
- }
- </style>
|