mirror of
https://gitee.com/lijingbo-2021/open-anylink-web.git
synced 2025-12-30 02:52:26 +00:00
重构:1)组件通信基于Store,2)输入组件改成quill
This commit is contained in:
@@ -15,8 +15,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"@vueup/vue-quill": "^1.2.0",
|
||||
"axios": "^1.7.4",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus": "^2.8.0",
|
||||
|
||||
729
pnpm-lock.yaml
generated
729
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,84 +1,83 @@
|
||||
<script setup>
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { onMounted, onBeforeUnmount, ref, shallowRef, watch } from 'vue'
|
||||
import { Editor } from '@wangeditor/editor-for-vue'
|
||||
import { messageStore } from '@/stores'
|
||||
import { QuillEditor } from '@vueup/vue-quill'
|
||||
import '@vueup/vue-quill/dist/vue-quill.snow.css'
|
||||
import { onMounted, ref, watch, onUpdated } from 'vue'
|
||||
import { userStore, messageStore } from '@/stores'
|
||||
|
||||
const mode = 'simple'
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef()
|
||||
const editorStyleRef = ref()
|
||||
const props = defineProps(['sessionInfo'])
|
||||
const props = defineProps(['draft'])
|
||||
const emit = defineEmits(['exportContent'])
|
||||
const messageData = messageStore()
|
||||
const userData = userStore()
|
||||
|
||||
// 内容 HTML
|
||||
const valueHtml = ref('')
|
||||
const content = [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: props.sessionInfo.draft || '', fontSize: '14px' }],
|
||||
lineHeight: 0.5
|
||||
}
|
||||
]
|
||||
const editorConfig = { placeholder: '' }
|
||||
const editorRef = ref()
|
||||
|
||||
onMounted(() => {
|
||||
// 给组件增加滚动条样式
|
||||
editorStyleRef.value.$el.querySelector('.w-e-scroll').classList.add('my-scrollbar')
|
||||
})
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
const handleCreated = (editor) => {
|
||||
editorRef.value = editor // 记录 editor 实例,重要!
|
||||
const getQuill = () => {
|
||||
return editorRef.value?.getQuill()
|
||||
}
|
||||
|
||||
const handleEnter = () => {
|
||||
const editor = editorRef.value
|
||||
const trimContent = editor.getText().trim()
|
||||
if (!trimContent) {
|
||||
const content = getQuill().getText().trim()
|
||||
if (!content) {
|
||||
ElMessage.warning('请勿发送空内容')
|
||||
valueHtml.value = ''
|
||||
getQuill().setText('')
|
||||
} else {
|
||||
emit('exportContent', trimContent)
|
||||
valueHtml.value = ''
|
||||
emit('exportContent', content)
|
||||
getQuill().setText('')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 给组件增加滚动条样式
|
||||
document.querySelector('.ql-editor').classList.add('my-scrollbar')
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
getQuill().setText(props.draft)
|
||||
})
|
||||
|
||||
// 监控session发生了切换
|
||||
watch(
|
||||
() => props.sessionInfo,
|
||||
() => userData.lastSessionId,
|
||||
(newValue, oldValue) => {
|
||||
valueHtml.value = newValue.draft || ''
|
||||
let content = getQuill().getText().trim()
|
||||
// 草稿若没发生变动,则不触发存储
|
||||
if (editorRef.value.getText().trim() !== oldValue.draft) {
|
||||
if (oldValue && content !== messageData.sessionList[oldValue].draft) {
|
||||
messageData.updateSession({
|
||||
...oldValue,
|
||||
draft: editorRef.value.getText().trim()
|
||||
sessionId: oldValue,
|
||||
draft: content
|
||||
})
|
||||
}
|
||||
getQuill().setText(messageData.sessionList[newValue].draft || '')
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
const options = {
|
||||
debug: false,
|
||||
modules: {
|
||||
toolbar: false,
|
||||
keyboard: {
|
||||
bindings: {
|
||||
enter: {
|
||||
key: 13,
|
||||
handler: handleEnter
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
placeholder: 'Enter发送 / Shift+Enter换行',
|
||||
theme: 'snow'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="input-editor">
|
||||
<Editor
|
||||
ref="editorStyleRef"
|
||||
<QuillEditor
|
||||
class="editor"
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
:defaultContent="content"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
@keyup.enter="handleEnter"
|
||||
/>
|
||||
ref="editorRef"
|
||||
:options="options"
|
||||
content-type="text"
|
||||
></QuillEditor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -88,8 +87,14 @@ watch(
|
||||
|
||||
.editor {
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
|
||||
.ql-editor {
|
||||
padding: 16px;
|
||||
padding-left: 16px;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,37 +2,65 @@ import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { msgUpdateSessionService } from '@/api/message'
|
||||
|
||||
// 消息功能相关需要缓存的数据
|
||||
// 消息功能相关需要缓存的数据,不持久化存储
|
||||
export const messageStore = defineStore('anyim-message', () => {
|
||||
/**
|
||||
* 格式:{sessionId_1: session_1, sessionId_2: session_2, ...}
|
||||
*/
|
||||
const sessionList = ref({})
|
||||
|
||||
const setSessionList = (sessions) => {
|
||||
sessionList.value = sessions
|
||||
}
|
||||
|
||||
const updateSession = (session) => {
|
||||
if (sessionList.value[session.sessionId]) {
|
||||
sessionList.value[session.sessionId] = session
|
||||
} else {
|
||||
sessionList.value = {
|
||||
...sessionList.value,
|
||||
[session.sessionId]: session
|
||||
}
|
||||
}
|
||||
// 加入新的会话(陌生人发消息,产生新的会话)
|
||||
const addSession = (session) => {
|
||||
sessionList.value[session.sessionId] = session
|
||||
}
|
||||
|
||||
msgUpdateSessionService({
|
||||
sessionId: session.sessionId,
|
||||
readMsgId: session.readMsgId,
|
||||
readTime: session.readTime,
|
||||
top: session.top,
|
||||
muted: session.muted,
|
||||
draft: session.draft
|
||||
})
|
||||
const updateSession = (obj) => {
|
||||
const mySession = sessionList.value[obj.sessionId]
|
||||
if (obj.readMsgId) mySession.readMsgId = obj.readMsgId
|
||||
if (obj.readTime) mySession.readTime = obj.readTime
|
||||
if (obj.lastMsgId) mySession.lastMsgId = obj.lastMsgId
|
||||
if (obj.lastMsgContent) mySession.lastMsgContent = obj.lastMsgContent
|
||||
if (obj.lastMsgTime) mySession.lastMsgTime = obj.lastMsgTime
|
||||
if (obj.unreadCount) mySession.unreadCount = obj.unreadCount
|
||||
if (obj.top) mySession.top = obj.top
|
||||
if (obj.muted) mySession.muted = obj.muted
|
||||
if ('draft' in obj) mySession.draft = obj.draft
|
||||
|
||||
let params = { sessionId: obj.sessionId }
|
||||
if (obj.top) params.top = obj.top
|
||||
if (obj.muted) params.muted = obj.muted
|
||||
if ('draft' in obj) params.draft = obj.draft
|
||||
|
||||
if (obj.top || obj.muted || 'draft' in obj) {
|
||||
msgUpdateSessionService(params)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式:{sessionId_1: msgRecord_1, sessionId_2: msgRecord_2, ...}
|
||||
*/
|
||||
const msgRecordList = ref({})
|
||||
|
||||
const setMsgRecordList = (msgRecords) => {
|
||||
msgRecordList.value = msgRecords
|
||||
}
|
||||
|
||||
const addMsgRecord = (msgRecord) => {
|
||||
msgRecordList.value[msgRecord.sessionId] = msgRecord
|
||||
}
|
||||
|
||||
return {
|
||||
sessionList,
|
||||
setSessionList,
|
||||
updateSession
|
||||
addSession,
|
||||
updateSession,
|
||||
|
||||
msgRecordList,
|
||||
setMsgRecordList,
|
||||
addMsgRecord
|
||||
}
|
||||
})
|
||||
|
||||
@@ -36,14 +36,9 @@ const asideWidthMin = 200
|
||||
const asideWidthMax = 500
|
||||
|
||||
const inputBoxHeight = ref(0)
|
||||
const inputBoxHeightMin = 350
|
||||
const inputBoxHeightMin = 200
|
||||
const inputBoxHeightMax = 500
|
||||
|
||||
const sessionList = ref({})
|
||||
const choosedSessionId = ref()
|
||||
const choosedSession = ref({})
|
||||
const msgRecords = ref([])
|
||||
|
||||
const isShowTopLoading = ref(true)
|
||||
const isTopLoading = ref(false)
|
||||
const loadMoreTips = ref('查看更多消息')
|
||||
@@ -51,16 +46,26 @@ const loadCursor = computed(() => {
|
||||
return isTopLoading.value ? 'auto' : 'pointer'
|
||||
})
|
||||
|
||||
const choosedSession = computed(() => {
|
||||
if (userData.lastSessionId)
|
||||
return messageData.sessionList[userData.lastSessionId]
|
||||
else
|
||||
return {}
|
||||
})
|
||||
const msgRecords = ref([])
|
||||
|
||||
const msgListDiv = ref()
|
||||
|
||||
onMounted(async () => {
|
||||
asideWidth.value = settingData.sessionListDrag[userData.user.account] || 200
|
||||
inputBoxHeight.value = settingData.inputBoxDrag[userData.user.account] || 350
|
||||
asideWidth.value = settingData.sessionListDrag[userData.user.account] || 300
|
||||
inputBoxHeight.value = settingData.inputBoxDrag[userData.user.account] || 300
|
||||
|
||||
const res = await msgChatSessionListService()
|
||||
messageData.setSessionList(res.data.data) //入缓存
|
||||
sessionList.value = messageData.sessionList
|
||||
choosedSessionId.value = userData.lastSessionId
|
||||
|
||||
if (userData.lastSessionId) {
|
||||
pullMsg()
|
||||
}
|
||||
|
||||
if (msgListDiv.value) {
|
||||
// 首次进到消息页面,不会有有值
|
||||
@@ -70,17 +75,17 @@ onMounted(async () => {
|
||||
|
||||
// 把sessionList转成数组,并按照lastMsgTime排序
|
||||
const sessionListSorted = computed(() => {
|
||||
if (!sessionList.value) {
|
||||
if (!messageData.sessionList) {
|
||||
return []
|
||||
}
|
||||
else {
|
||||
let sessionArr = Object.values(sessionList.value)
|
||||
let sessionArr = Object.values(messageData.sessionList)
|
||||
return sessionArr.sort((a, b) => b.lastMsgTime - a.lastMsgTime)
|
||||
}
|
||||
})
|
||||
|
||||
const showName = computed(() => {
|
||||
switch (choosedSession.value.sessionType) {
|
||||
switch (choosedSession.value?.sessionType) {
|
||||
case MsgType.CHAT:
|
||||
return choosedSession.value.objectInfo.nickName
|
||||
case MsgType.GROUP_CHAT:
|
||||
@@ -91,7 +96,7 @@ const showName = computed(() => {
|
||||
})
|
||||
|
||||
const showId = computed(() => {
|
||||
switch (choosedSession.value.sessionType) {
|
||||
switch (choosedSession.value?.sessionType) {
|
||||
case MsgType.CHAT:
|
||||
return choosedSession.value.objectInfo.account
|
||||
case MsgType.GROUP_CHAT:
|
||||
@@ -126,15 +131,35 @@ const onInputBoxDragUpdate = ({ height }) => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleIsChoosed = (session) => {
|
||||
choosedSessionId.value = session.sessionId // sessionId变化会引发watch
|
||||
const pullMsg = () => {
|
||||
msgChatPullMsgService({
|
||||
sessionId: userData.lastSessionId,
|
||||
readMsgId: choosedSession.value.readMsgId,
|
||||
readTime: choosedSession.value.readTime,
|
||||
pageSize: 20
|
||||
})
|
||||
.then((res) => {
|
||||
msgRecords.value = res.data.data.msgList
|
||||
messageData.updateSession({
|
||||
sessionId: userData.lastSessionId,
|
||||
readMsgId: res.data.data.lastMsgId,
|
||||
readTime: new Date(),
|
||||
lastMsgId: res.data.data.lastMsgId,
|
||||
lastMsgContent: res.data.data.msgList.content,
|
||||
lastMsgTime: res.data.data.msgList.msgTime,
|
||||
unreadCount: 0
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 表示有个session被选中了
|
||||
const handleIsChoosed = (exportSession) => {
|
||||
userData.setLastSessionId(exportSession.sessionId)
|
||||
pullMsg()
|
||||
}
|
||||
|
||||
const handleSwitchTag = (obj) => {
|
||||
messageData.updateSession({
|
||||
...choosedSession.value,
|
||||
...obj
|
||||
})
|
||||
messageData.updateSession(obj)
|
||||
}
|
||||
|
||||
const handleExportContent = (content) => {
|
||||
@@ -142,22 +167,21 @@ const handleExportContent = (content) => {
|
||||
wsConnect.sendMsg(showId.value, choosedSession.value.sessionType, content, (deliveredMsg) => {
|
||||
|
||||
const now = new Date()
|
||||
|
||||
messageData.updateSession({
|
||||
...choosedSession.value,
|
||||
readMsgId: deliveredMsg.body.msgId,
|
||||
sessionId: userData.lastSessionId,
|
||||
readMsgId: deliveredMsg.body.msgId, // 发消息视为已经读到最后一条消息(自己发的)
|
||||
readTime: now,
|
||||
lastMsgId: deliveredMsg.body.msgId,
|
||||
lastMsgId: deliveredMsg.body.msgId, // lastMsgId = 最后一条消息(自己发的)
|
||||
lastMsgContent: content,
|
||||
lastMsgTime: now,
|
||||
draft: ''
|
||||
unreadCount: 0, // readMsgId = lastMsgId = 最后一条消息(自己发的),因此未读是0
|
||||
draft: '' //草稿意味着要清空
|
||||
})
|
||||
|
||||
// 如果当前sessionid和这个“已发送”消息的sessionId,更新到msgRecords中
|
||||
if (choosedSessionId.value === deliveredMsg.body.sessionId) {
|
||||
|
||||
if (userData.lastSessionId === deliveredMsg.body.sessionId) {
|
||||
msgRecords.value.push({
|
||||
sessionId: choosedSessionId.value,
|
||||
sessionId: userData.lastSessionId,
|
||||
msgId: deliveredMsg.body.msgId,
|
||||
fromId: userData.user.account,
|
||||
msgType: choosedSession.value.sessionType,
|
||||
@@ -173,39 +197,6 @@ const onLoadMore = () => {
|
||||
loadMoreTips.value = ''
|
||||
}
|
||||
|
||||
// watch到哪个,表示哪个会话被选中
|
||||
watch(choosedSessionId, (newValue) => {
|
||||
userData.setLastSessionId(newValue)
|
||||
choosedSession.value = sessionList.value[newValue]
|
||||
|
||||
msgChatPullMsgService({
|
||||
sessionId: choosedSessionId.value,
|
||||
lastMsgId: sessionList.value[choosedSessionId.value].readMsgId,
|
||||
lastPullTime: sessionList.value[choosedSessionId.value].readTime,
|
||||
pageSize: 20
|
||||
})
|
||||
.then((res) => {
|
||||
msgRecords.value = res.data.data.msgList
|
||||
|
||||
if (res.data.data.lastMsgId > choosedSession.value.readMsgId) {
|
||||
const now = new Date()
|
||||
|
||||
//这三行能不能省,下面的只是改了Store的值,本地的choosedSession没有改
|
||||
choosedSession.value.readMsgId = res.data.data.lastMsgId
|
||||
choosedSession.value.readTime = now
|
||||
choosedSession.value.unreadCount = 0;
|
||||
|
||||
messageData.updateSession({
|
||||
...choosedSession.value,
|
||||
sessionId: choosedSessionId.value,
|
||||
readMsgId: res.data.data.lastMsgId,
|
||||
readTime: now,
|
||||
unreadCount: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
watch(msgRecords, () => {
|
||||
if (msgRecords.value) {
|
||||
msgRecords.value = msgRecords.value.sort((a, b) => a.msgId - b.msgId)
|
||||
@@ -256,7 +247,7 @@ const msgListReachBottom = () => {
|
||||
<el-main class="msg-box">
|
||||
<el-image
|
||||
class="backgroup-image"
|
||||
v-if="!choosedSessionId"
|
||||
v-if="!userData.lastSessionId"
|
||||
:src="backgroupImage"
|
||||
fit="cover"
|
||||
></el-image>
|
||||
@@ -265,7 +256,7 @@ const msgListReachBottom = () => {
|
||||
<el-header class="header bdr-b">
|
||||
<div class="show-name-id">
|
||||
<span class="show-name">{{ showName }}</span>
|
||||
<span v-if="choosedSession.sessionType === MsgType.CHAT" class="show-id">{{
|
||||
<span v-if="choosedSession?.sessionType === MsgType.CHAT" class="show-id">{{
|
||||
showId
|
||||
}}</span>
|
||||
</div>
|
||||
@@ -297,7 +288,7 @@ const msgListReachBottom = () => {
|
||||
v-for="(item, index) in msgRecords"
|
||||
:key="index"
|
||||
:msg="item"
|
||||
:obj="choosedSession.objectInfo"
|
||||
:obj="choosedSession?.objectInfo"
|
||||
:lastMsgTime="getLastMsgTime(index)"
|
||||
></MessageItem>
|
||||
</div>
|
||||
@@ -351,7 +342,7 @@ const msgListReachBottom = () => {
|
||||
</el-header>
|
||||
<el-main class="input-box-main">
|
||||
<InputEditor
|
||||
:sessionInfo="choosedSession"
|
||||
:draft="choosedSession?.draft"
|
||||
@exportContent="handleExportContent"
|
||||
></InputEditor>
|
||||
</el-main>
|
||||
|
||||
Reference in New Issue
Block a user