perf: 对话顶部提示

This commit is contained in:
kuaifan 2023-02-19 08:47:14 +08:00
parent 66f0bef271
commit 42e54d8a6e
7 changed files with 102 additions and 53 deletions

View File

@ -1033,8 +1033,9 @@ class DialogController extends AbstractController
* *
* @apiParam {Number} dialog_id 会话ID * @apiParam {Number} dialog_id 会话ID
* @apiParam {String} type 类型 * @apiParam {String} type 类型
* - read * - read: 已读
* - unread * - unread: 未读
* @apiParam {Number} [after_msg_id] 仅标记已读指定之后(含)的消息
* *
* @apiSuccess {Number} ret 返回状态码1正确、0错误 * @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述) * @apiSuccess {String} msg 返回信息(错误描述)
@ -1045,6 +1046,7 @@ class DialogController extends AbstractController
$user = User::auth(); $user = User::auth();
$dialogId = intval(Request::input('dialog_id')); $dialogId = intval(Request::input('dialog_id'));
$type = Request::input('type'); $type = Request::input('type');
$afterMsgId = intval(Request::input('after_msg_id'));
$dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first(); $dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first();
if (!$dialogUser) { if (!$dialogUser) {
return Base::retError("会话不存在"); return Base::retError("会话不存在");
@ -1054,27 +1056,31 @@ class DialogController extends AbstractController
]; ];
switch ($type) { switch ($type) {
case 'read': case 'read':
$data['mark_unread'] = 0;
$data['unread'] = 0; $data['unread'] = 0;
$data['first_umid'] = 0; $data['mention'] = 0;
WebSocketDialogMsgRead::whereUserid($user->userid) $builder = WebSocketDialogMsgRead::whereUserid($user->userid)->whereReadAt(null)->whereDialogId($dialogId);
->whereReadAt(null) if ($afterMsgId > 0) {
->whereDialogId($dialogId) $unBuilder = $builder->clone()->where('msg_id', '<', $afterMsgId);
->chunkById(100, function ($list) { $data['unread'] = $unBuilder->count();
WebSocketDialogMsgRead::onlyMarkRead($list); $data['mention'] = $data['unread'] > 0 ? $unBuilder->whereMention(1)->count() : 0;
}); $builder->where('msg_id', '>=', $afterMsgId);
$dialogUser->mark_unread = 0; }
$dialogUser->save(); $builder->chunkById(100, function ($list) {
WebSocketDialogMsgRead::onlyMarkRead($list);
});
$data['position_msgs'] = WebSocketDialog::find($dialogId)?->getPositionMsgs($user->userid) ?: [];
break; break;
case 'unread': case 'unread':
$dialogUser->mark_unread = 1; $data['mark_unread'] = 1;
$dialogUser->save();
break; break;
default: default:
return Base::retError("参数错误"); return Base::retError("参数错误");
} }
$data['mark_unread'] = $dialogUser->mark_unread; $dialogUser->mark_unread = $data['mark_unread'];
$dialogUser->save();
return Base::retSuccess("success", $data); return Base::retSuccess("success", $data);
} }

View File

@ -76,16 +76,16 @@ class WebSocketDialog extends AbstractModel
// 最后消息 // 最后消息
$this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first(); $this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
// 未读信息 // 未读信息
$unreadBuilder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null); $unBuilder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null);
$this->unread = $unreadBuilder->count(); $this->unread = $unBuilder->count();
$this->mention = 0; $this->mention = 0;
$this->last_umid = 0; $this->last_umid = 0;
$this->first_umid = 0; // 第一条未读消息 $this->position_msgs = [];
if ($this->unread > 0) { if ($this->unread > 0) {
$this->mention = $unreadBuilder->clone()->whereMention(1)->count(); $this->mention = $unBuilder->clone()->whereMention(1)->count();
$this->last_umid = intval($unreadBuilder->clone()->orderByDesc('msg_id')->value('msg_id')); $this->last_umid = intval($unBuilder->clone()->orderByDesc('msg_id')->value('msg_id'));
if ($hasData === true) { 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'); $this->mark_unread = $this->mark_unread ?? $dialogUserFun('mark_unread');
@ -156,6 +156,34 @@ class WebSocketDialog extends AbstractModel
return $this; 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组 * @param int|array $userid 加入的会员ID或会员ID组

View File

@ -99,10 +99,11 @@
</div> </div>
<!--顶部提示--> <!--顶部提示-->
<div v-if="beforeUnread > 0" class="dialog-top" :class="{'down': tagShow}"> <div v-if="positionMsg" class="dialog-position" :class="{'down': tagShow}">
<div class="top-unread" @click="goBeforeUnread"> <div class="position-label" @click="onPositionMark">
<Icon v-if="beforeLoad" type="ios-loading" class="icon-loading"></Icon> <Icon v-if="positionLoad > 0" type="ios-loading" class="icon-loading"></Icon>
<span>{{$L(`未读消息${beforeUnread}`)}}</span> <i v-else class="taskfont">&#xe624;</i>
{{positionMsg.label}}
</div> </div>
</div> </div>
@ -543,7 +544,7 @@ export default {
scrollAction: 0, scrollAction: 0,
scrollTmp: 0, scrollTmp: 0,
beforeLoad: false, positionLoad: 0,
} }
}, },
@ -775,16 +776,24 @@ export default {
return null return null
}, },
beforeUnread() { positionMsg() {
const {unread, first_umid} = this.dialogData const {unread, position_msgs} = this.dialogData
if (unread > 0 if (unread === 0 || this.allMsgs.length === 0 ||position_msgs.length === 0) {
&& first_umid > 0 return null
&& this.allMsgs.length > 0
&& this.allMsgs.findIndex(({id}) => id == first_umid) === -1) {
return unread
} }
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: { watch: {
@ -2256,22 +2265,26 @@ export default {
} }
}, },
goBeforeUnread() { onPositionMark() {
if (this.beforeUnread === 0 || this.beforeLoad) { if (this.positionLoad > 0) {
return; return;
} }
// //
this.beforeLoad = true this.positionLoad++
const {first_umid} = this.dialogData const {msg_id} = this.positionMsg;
this.$store.dispatch("dialogMsgMark", { this.$store.dispatch("dialogMsgMark", {
dialog_id: this.dialogId, dialog_id: this.dialogId,
type: 'read' type: 'read',
after_msg_id: msg_id,
}).then(_ => { }).then(_ => {
this.onPositionId(first_umid) this.positionLoad++
this.onPositionId(msg_id).finally(_ => {
this.positionLoad--
})
}).catch(({msg}) => { }).catch(({msg}) => {
$A.modalError(msg) $A.modalError(msg)
}).finally(_ => { }).finally(_ => {
this.beforeLoad = false this.positionLoad--
}) })
}, },

View File

@ -1086,26 +1086,29 @@
} }
} }
.dialog-top { .dialog-position {
position: absolute; position: absolute;
top: 78px; top: 100px;
left: 0;
right: 0; right: 0;
z-index: 1; z-index: 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: flex-end;
.top-unread { .position-label {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 6px 12px; padding: 5px 10px;
border-radius: 4px; border-radius: 18px 0 0 18px;
box-shadow: 0 2px 8px 0 rgba($primary-text-color, 0.33); color: #ffffff;
background: #fff; background-color: $primary-color;
cursor: pointer; cursor: pointer;
> i { > i {
margin-right: 4px; margin-right: 4px;
width: 14px;
height: 14px;
font-size: 14px;
line-height: 14px;
} }
} }
} }
@ -1625,10 +1628,9 @@
} }
} }
} }
.dialog-top { .dialog-position {
top: 68px;
&.down { &.down {
top: 98px; top: 130px;
} }
} }
.dialog-footer { .dialog-footer {