完成优化聊天

This commit is contained in:
kuaifan 2021-06-23 00:06:36 +08:00
parent 2f9cd34ad2
commit bbb73544ea
9 changed files with 65 additions and 37 deletions

View File

@ -107,16 +107,17 @@ class DialogController extends AbstractController
// //
$list = WebSocketDialogMsg::whereDialogId($dialog_id)->orderByDesc('id')->paginate(Base::getPaginate(100, 50)); $list = WebSocketDialogMsg::whereDialogId($dialog_id)->orderByDesc('id')->paginate(Base::getPaginate(100, 50));
$list->transform(function (WebSocketDialogMsg $item) use ($user) { $list->transform(function (WebSocketDialogMsg $item) use ($user) {
$item->r = $item->userid === $user->userid ? null : WebSocketDialogMsgRead::whereMsgId($item->id)->whereUserid($user->userid)->first(); $item->is_read = $item->userid === $user->userid || WebSocketDialogMsgRead::whereMsgId($item->id)->whereUserid($user->userid)->value('read_at');
return $item; return $item;
}); });
// //
$data = $list->toArray(); $data = $list->toArray();
$data['dialog'] = WebSocketDialog::formatData($dialog, $user->userid); if ($list->currentPage() === 1) {
// $data['dialog'] = WebSocketDialog::formatData($dialog, $user->userid);
$user->dialog_id = $dialog->id; //
$user->save(); $user->dialog_id = $dialog->id;
// $user->save();
}
return Base::retSuccess('success', $data); return Base::retSuccess('success', $data);
} }

View File

@ -53,7 +53,6 @@
</template> </template>
<script> <script>
import {mapState} from "vuex";
import WCircle from "../../../components/WCircle"; import WCircle from "../../../components/WCircle";
export default { export default {
@ -78,9 +77,11 @@ export default {
} }
}, },
computed: { activated() {
...mapState(['userId']), this.msgRead()
},
computed: {
readList() { readList() {
return this.read_list.filter(({read_at}) => read_at) return this.read_list.filter(({read_at}) => read_at)
}, },
@ -92,14 +93,29 @@ export default {
watch: { watch: {
msgData: { msgData: {
handler(data) { handler() {
this.$store.dispatch("dialogMsgRead", data); this.msgRead();
}, },
immediate: true, immediate: true,
} }
}, },
methods: { methods: {
msgRead() {
if (this.msgData._r === true) {
return;
}
this.msgData._r = true;
//
this.$nextTick(() => {
if (!this.$el.offsetParent) {
this.msgData._r = false;
return
}
this.$store.dispatch("dialogMsgRead", this.msgData);
})
},
popperShow() { popperShow() {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'dialog/msg/readlist', url: 'dialog/msg/readlist',

View File

@ -356,7 +356,7 @@ export default {
loadNextPage() { loadNextPage() {
let topId = this.dialogMsgList[0].id; let topId = this.dialogMsgList[0].id;
this.$store.dispatch('getDialogMsgNextPage').then(() => { this.$store.dispatch('getDialogMsgNextPage', this.dialogId).then(() => {
this.$nextTick(() => { this.$nextTick(() => {
this.topId = topId; this.topId = topId;
let dom = document.getElementById("view_" + topId); let dom = document.getElementById("view_" + topId);

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="project-dialog"> <div class="project-dialog">
<DialogWrapper :dialog-id="projectData.dialog_id" class="project-dialog-wrapper"> <DialogWrapper v-if="dialogs.length > 0" :dialog-id="projectData.dialog_id" class="project-dialog-wrapper">
<div slot="head"> <div slot="head">
<div class="dialog-user"> <div class="dialog-user">
<div class="member-head"> <div class="member-head">
@ -22,7 +22,7 @@
</template> </template>
<script> <script>
import {mapGetters} from "vuex"; import {mapGetters, mapState} from "vuex";
import DialogWrapper from "./DialogWrapper"; import DialogWrapper from "./DialogWrapper";
export default { export default {
@ -35,6 +35,7 @@ export default {
}, },
computed: { computed: {
...mapState(['dialogs']),
...mapGetters(['projectData']) ...mapGetters(['projectData'])
}, },
} }

View File

@ -332,7 +332,7 @@
</div> </div>
<div class="task-dialog" :style="dialogStyle"> <div class="task-dialog" :style="dialogStyle">
<template v-if="taskDetail.dialog_id > 0"> <template v-if="taskDetail.dialog_id > 0">
<DialogWrapper ref="dialog" :dialog-id="taskDetail.dialog_id"> <DialogWrapper ref="dialog" v-if="dialogs.length" :dialog-id="taskDetail.dialog_id">
<div slot="head" class="head"> <div slot="head" class="head">
<Icon class="icon" type="ios-chatbubbles-outline" /> <Icon class="icon" type="ios-chatbubbles-outline" />
<div class="nav"> <div class="nav">
@ -466,7 +466,17 @@ export default {
}, },
computed: { computed: {
...mapState(['userId', 'projects', 'columns', 'taskId', 'taskSubs', 'taskContents', 'taskFiles', 'taskPriority']), ...mapState([
'userId',
'projects',
'columns',
'taskId',
'taskSubs',
'taskContents',
'taskFiles',
'taskPriority',
'dialogs'
]),
projectName() { projectName() {
if (!this.taskDetail.project_id) { if (!this.taskDetail.project_id) {

View File

@ -60,7 +60,7 @@
</div> </div>
<div class="messenger-msg"> <div class="messenger-msg">
<DialogWrapper v-if="dialogId > 0" :dialogId="dialogId" @on-active="scrollIntoActive"/> <DialogWrapper v-if="dialogs.length > 0 && dialogId > 0" :dialogId="dialogId" @on-active="scrollIntoActive"/>
<div v-else class="dialog-no"> <div v-else class="dialog-no">
<div class="dialog-no-icon"><Icon type="ios-chatbubbles" /></div> <div class="dialog-no-icon"><Icon type="ios-chatbubbles" /></div>
<div class="dialog-no-text">{{$L('选择一个会话开始聊天')}}</div> <div class="dialog-no-text">{{$L('选择一个会话开始聊天')}}</div>
@ -96,7 +96,6 @@ export default {
}, },
activated() { activated() {
this.$store.dispatch("getDialogs");
this.openDialogStorage(); this.openDialogStorage();
}, },

View File

@ -1080,7 +1080,7 @@ export default {
* @param dialog_id * @param dialog_id
*/ */
getDialogMsgs({state, dispatch}, dialog_id) { getDialogMsgs({state, dispatch}, dialog_id) {
const dialog = state.dialogs.filter(({id}) => id == dialog_id); const dialog = state.dialogs.find(({id}) => id == dialog_id);
if (!dialog) { if (!dialog) {
return; return;
} }
@ -1099,11 +1099,14 @@ export default {
}, },
}).then(result => { }).then(result => {
dialog.loading = false; dialog.loading = false;
dialog.currentPage = result.data.current_page;
dialog.hasMorePages = !!result.data.next_page_url;
const ids = result.data.data.map(({id}) => id) const ids = result.data.data.map(({id}) => id)
if (ids.length == 0) { if (ids.length == 0) {
return; return;
} }
state.dialogMsgs = state.dialogMsgs.filter((item) => item.dialog_id != dialog_id || ids.includes(item.id)); state.dialogMsgs = state.dialogMsgs.filter((item) => item.dialog_id != dialog_id || ids.includes(item.id));
dispatch("saveDialog", result.data.dialog);
dispatch("saveDialogMsg", result.data.data); dispatch("saveDialogMsg", result.data.data);
}).catch(e => { }).catch(e => {
console.error(e); console.error(e);
@ -1119,7 +1122,7 @@ export default {
*/ */
getDialogMsgNextPage({state, dispatch}, dialog_id) { getDialogMsgNextPage({state, dispatch}, dialog_id) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
const dialog = state.dialogs.filter(({id}) => id == dialog_id); const dialog = state.dialogs.find(({id}) => id == dialog_id);
if (!dialog) { if (!dialog) {
return; return;
} }
@ -1140,7 +1143,9 @@ export default {
}, },
}).then(result => { }).then(result => {
dialog.loading = false; dialog.loading = false;
dispatch("saveDialogMsg", result.data); dialog.currentPage = result.data.current_page;
dialog.hasMorePages = !!result.data.next_page_url;
dispatch("saveDialogMsg", result.data.data);
resolve(result) resolve(result)
}).catch(e => { }).catch(e => {
console.error(e); console.error(e);
@ -1154,23 +1159,19 @@ export default {
* 发送已阅消息 * 发送已阅消息
* @param state * @param state
* @param dispatch * @param dispatch
* @param msgData * @param data
*/ */
dialogMsgRead({state, dispatch}, msgData) { dialogMsgRead({state, dispatch}, data) {
if (msgData.userid == state.userId) return; if (data.userid == state.userId) return;
if (!state.method.isJson(msgData.r)) msgData.r = {}; if (data.is_read === true) return;
data.is_read = true;
// //
const {id, dialog_id, r} = msgData; let dialog = state.dialogs.find(({id}) => id == data.dialog_id);
if (r.read_at) {
return;
}
r.read_at = state.method.formatDate('Y-m-d H:i:s');
let dialog = state.dialogs.find(({id}) => id == dialog_id);
if (dialog && dialog.unread > 0) { if (dialog && dialog.unread > 0) {
dialog.unread-- dialog.unread--
} }
// //
state.wsReadWaitList.push(id); state.wsReadWaitList.push(data.id);
clearTimeout(state.wsReadTimeout); clearTimeout(state.wsReadTimeout);
state.wsReadTimeout = setTimeout(() => { state.wsReadTimeout = setTimeout(() => {
dispatch("websocketSend", { dispatch("websocketSend", {

View File

@ -30,7 +30,8 @@ export default {
} }
} }
return { return {
columns: [] columns: [],
project_user: []
}; };
}, },

View File

@ -131,8 +131,6 @@ const method = {
let keyName = '__state__'; let keyName = '__state__';
if (key.substring(0, 5) === 'cache') { if (key.substring(0, 5) === 'cache') {
keyName = '__state:' + key + '__'; keyName = '__state:' + key + '__';
} else if (key.substring(0, 7) === 'boolean') {
keyName = '__state:Boolean__';
} }
if (typeof value === 'undefined') { if (typeof value === 'undefined') {
return this.loadFromlLocal(key, '', keyName); return this.loadFromlLocal(key, '', keyName);
@ -228,13 +226,14 @@ const state = { method };
// 数据缓存 // 数据缓存
state.cacheUserBasic = state.method.getStorageJson("cacheUserBasic"); state.cacheUserBasic = state.method.getStorageJson("cacheUserBasic");
state.cacheDialogMsg = state.method.getStorageJson("cacheDialogMsg"); state.cacheDialogs = state.method.getStorageArray("cacheDialogs");
state.cacheDialogMsgs = state.method.getStorageArray("cacheDialogMsgs");
state.cacheProjects = state.method.getStorageArray("cacheProjects"); state.cacheProjects = state.method.getStorageArray("cacheProjects");
state.cacheColumns = state.method.getStorageArray("cacheColumns"); state.cacheColumns = state.method.getStorageArray("cacheColumns");
state.cacheTasks = state.method.getStorageArray("cacheTasks"); state.cacheTasks = state.method.getStorageArray("cacheTasks");
state.cacheTaskSubs = state.method.getStorageArray("cacheTaskSubs"); state.cacheTaskSubs = state.method.getStorageArray("cacheTaskSubs");
state.cacheTablePanel = state.method.getStorageArray("cacheTablePanel"); state.cacheTablePanel = state.method.getStorageArray("cacheTablePanel");
state.showCompletedTask = state.method.getStorageBoolean("boolean:showCompletedTask") state.showCompletedTask = state.method.getStorageBoolean("showCompletedTask")
// Ajax // Ajax
state.ajaxLoadNum = 0; state.ajaxLoadNum = 0;