diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index 7d4aea0c7..ab9a31945 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -1033,8 +1033,9 @@ class DialogController extends AbstractController * * @apiParam {Number} dialog_id 会话ID * @apiParam {String} type 类型 - * - read - * - unread + * - read: 已读 + * - unread: 未读 + * @apiParam {Number} [after_msg_id] 仅标记已读指定之后(含)的消息 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) * @apiSuccess {String} msg 返回信息(错误描述) @@ -1045,6 +1046,7 @@ class DialogController extends AbstractController $user = User::auth(); $dialogId = intval(Request::input('dialog_id')); $type = Request::input('type'); + $afterMsgId = intval(Request::input('after_msg_id')); $dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first(); if (!$dialogUser) { return Base::retError("会话不存在"); @@ -1054,27 +1056,31 @@ class DialogController extends AbstractController ]; switch ($type) { case 'read': + $data['mark_unread'] = 0; $data['unread'] = 0; - $data['first_umid'] = 0; - WebSocketDialogMsgRead::whereUserid($user->userid) - ->whereReadAt(null) - ->whereDialogId($dialogId) - ->chunkById(100, function ($list) { - WebSocketDialogMsgRead::onlyMarkRead($list); - }); - $dialogUser->mark_unread = 0; - $dialogUser->save(); + $data['mention'] = 0; + $builder = WebSocketDialogMsgRead::whereUserid($user->userid)->whereReadAt(null)->whereDialogId($dialogId); + if ($afterMsgId > 0) { + $unBuilder = $builder->clone()->where('msg_id', '<', $afterMsgId); + $data['unread'] = $unBuilder->count(); + $data['mention'] = $data['unread'] > 0 ? $unBuilder->whereMention(1)->count() : 0; + $builder->where('msg_id', '>=', $afterMsgId); + } + $builder->chunkById(100, function ($list) { + WebSocketDialogMsgRead::onlyMarkRead($list); + }); + $data['position_msgs'] = WebSocketDialog::find($dialogId)?->getPositionMsgs($user->userid) ?: []; break; case 'unread': - $dialogUser->mark_unread = 1; - $dialogUser->save(); + $data['mark_unread'] = 1; break; default: return Base::retError("参数错误"); } - $data['mark_unread'] = $dialogUser->mark_unread; + $dialogUser->mark_unread = $data['mark_unread']; + $dialogUser->save(); return Base::retSuccess("success", $data); } diff --git a/app/Models/WebSocketDialog.php b/app/Models/WebSocketDialog.php index 8b8ceab51..382d49865 100644 --- a/app/Models/WebSocketDialog.php +++ b/app/Models/WebSocketDialog.php @@ -76,16 +76,16 @@ class WebSocketDialog extends AbstractModel // 最后消息 $this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first(); // 未读信息 - $unreadBuilder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null); - $this->unread = $unreadBuilder->count(); + $unBuilder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null); + $this->unread = $unBuilder->count(); $this->mention = 0; $this->last_umid = 0; - $this->first_umid = 0; // 第一条未读消息 + $this->position_msgs = []; if ($this->unread > 0) { - $this->mention = $unreadBuilder->clone()->whereMention(1)->count(); - $this->last_umid = intval($unreadBuilder->clone()->orderByDesc('msg_id')->value('msg_id')); + $this->mention = $unBuilder->clone()->whereMention(1)->count(); + $this->last_umid = intval($unBuilder->clone()->orderByDesc('msg_id')->value('msg_id')); if ($hasData === true) { - $this->first_umid = intval($unreadBuilder->clone()->orderBy('msg_id')->value('msg_id')); + $this->position_msgs = $this->getPositionMsgs($userid); } } $this->mark_unread = $this->mark_unread ?? $dialogUserFun('mark_unread'); @@ -156,6 +156,34 @@ class WebSocketDialog extends AbstractModel return $this; } + /** + * 获取定位消息 + * @param $userid + * @return array[] + */ + public function getPositionMsgs($userid) + { + $builder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null); + $array = []; + // @我的消息 + $mention_id = intval($builder->clone()->whereMention(1)->orderByDesc('msg_id')->value('msg_id')); + if ($mention_id > 0) { + $array[] = [ + 'msg_id' => $mention_id, + 'label' => Base::Lang('@我的消息'), + ]; + } + // 最早一条未读消息 + $first_id = intval($builder->clone()->orderBy('msg_id')->value('msg_id')); + if ($first_id > 0) { + $array[] = [ + 'msg_id' => $first_id, + 'label' => 'unread' + ]; + } + return $array; + } + /** * 加入聊天室 * @param int|array $userid 加入的会员ID或会员ID组 diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index 1f5636971..74c7bdd3a 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -99,10 +99,11 @@ -
-
- - {{$L(`未读消息${beforeUnread}条`)}} +
+
+ + + {{positionMsg.label}}
@@ -543,7 +544,7 @@ export default { scrollAction: 0, scrollTmp: 0, - beforeLoad: false, + positionLoad: 0, } }, @@ -775,16 +776,24 @@ export default { return null }, - beforeUnread() { - const {unread, first_umid} = this.dialogData - if (unread > 0 - && first_umid > 0 - && this.allMsgs.length > 0 - && this.allMsgs.findIndex(({id}) => id == first_umid) === -1) { - return unread + positionMsg() { + const {unread, position_msgs} = this.dialogData + if (unread === 0 || this.allMsgs.length === 0 ||position_msgs.length === 0) { + return null } - return 0 - } + const item = position_msgs.sort((a, b) => { + return b.msg_id - a.msg_id + })[0] + if (this.allMsgs.findIndex(({id}) => id == item.msg_id) === -1) { + if (item.label === 'unread') { + return Object.assign(item, { + 'label': this.$L(`未读消息${unread}条`) + }) + } + return item + } + return null + }, }, watch: { @@ -2256,22 +2265,26 @@ export default { } }, - goBeforeUnread() { - if (this.beforeUnread === 0 || this.beforeLoad) { + onPositionMark() { + if (this.positionLoad > 0) { return; } // - this.beforeLoad = true - const {first_umid} = this.dialogData + this.positionLoad++ + const {msg_id} = this.positionMsg; this.$store.dispatch("dialogMsgMark", { dialog_id: this.dialogId, - type: 'read' + type: 'read', + after_msg_id: msg_id, }).then(_ => { - this.onPositionId(first_umid) + this.positionLoad++ + this.onPositionId(msg_id).finally(_ => { + this.positionLoad-- + }) }).catch(({msg}) => { $A.modalError(msg) }).finally(_ => { - this.beforeLoad = false + this.positionLoad-- }) }, diff --git a/resources/assets/sass/pages/components/dialog-wrapper.scss b/resources/assets/sass/pages/components/dialog-wrapper.scss index 78eb6116c..abaa2252e 100644 --- a/resources/assets/sass/pages/components/dialog-wrapper.scss +++ b/resources/assets/sass/pages/components/dialog-wrapper.scss @@ -1086,26 +1086,29 @@ } } - .dialog-top { + .dialog-position { position: absolute; - top: 78px; - left: 0; + top: 100px; right: 0; z-index: 1; display: flex; align-items: center; - justify-content: center; - .top-unread { + justify-content: flex-end; + .position-label { display: flex; align-items: center; justify-content: center; - padding: 6px 12px; - border-radius: 4px; - box-shadow: 0 2px 8px 0 rgba($primary-text-color, 0.33); - background: #fff; + padding: 5px 10px; + border-radius: 18px 0 0 18px; + color: #ffffff; + background-color: $primary-color; cursor: pointer; > i { margin-right: 4px; + width: 14px; + height: 14px; + font-size: 14px; + line-height: 14px; } } } @@ -1625,10 +1628,9 @@ } } } - .dialog-top { - top: 68px; + .dialog-position { &.down { - top: 98px; + top: 130px; } } .dialog-footer { diff --git a/resources/assets/statics/public/css/fonts/taskfont/iconfont.ttf b/resources/assets/statics/public/css/fonts/taskfont/iconfont.ttf index 1f80b9b6a..4234d2c8b 100644 Binary files a/resources/assets/statics/public/css/fonts/taskfont/iconfont.ttf and b/resources/assets/statics/public/css/fonts/taskfont/iconfont.ttf differ diff --git a/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff b/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff index f4e782446..a88e1dd4b 100644 Binary files a/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff and b/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff differ diff --git a/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff2 b/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff2 index 192ba3adf..004b8695b 100644 Binary files a/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff2 and b/resources/assets/statics/public/css/fonts/taskfont/iconfont.woff2 differ