mirror of
https://github.com/kuaifan/dootask.git
synced 2026-03-03 16:02:08 +00:00
perf: 支持查看回复列表
This commit is contained in:
parent
13ca1b18d6
commit
66b7a00b5c
@ -279,9 +279,11 @@ class DialogController extends AbstractController
|
||||
* @apiName msg__list
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {Number} [position_id] 此消息ID前后的数据,优先级1
|
||||
* @apiParam {Number} [prev_id] 此消息ID之前的数据,优先级2
|
||||
* @apiParam {Number} [next_id] 此消息ID之后的数据,优先级3
|
||||
* @apiParam {Number} msg_id 消息ID
|
||||
* @apiParam {Number} [position_id] 此消息ID前后的数据
|
||||
* @apiParam {Number} [prev_id] 此消息ID之前的数据
|
||||
* @apiParam {Number} [next_id] 此消息ID之后的数据
|
||||
* - position_id、prev_id、next_id 只有一个有效,优先循序为:position_id > prev_id > next_id
|
||||
*
|
||||
* @apiParam {Number} [take] 获取条数,默认:50,最大:100
|
||||
*
|
||||
@ -295,6 +297,7 @@ class DialogController extends AbstractController
|
||||
$user = User::auth();
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$msg_id = intval(Request::input('msg_id'));
|
||||
$position_id = intval(Request::input('position_id'));
|
||||
$prev_id = intval(Request::input('prev_id'));
|
||||
$next_id = intval(Request::input('next_id'));
|
||||
@ -302,6 +305,7 @@ class DialogController extends AbstractController
|
||||
$data = [];
|
||||
//
|
||||
$dialog = WebSocketDialog::checkDialog($dialog_id);
|
||||
$reDialog = true;
|
||||
//
|
||||
$builder = WebSocketDialogMsg::select([
|
||||
'web_socket_dialog_msgs.*',
|
||||
@ -313,6 +317,11 @@ class DialogController extends AbstractController
|
||||
->on('read.msg_id', '=', 'web_socket_dialog_msgs.id');
|
||||
})->where('web_socket_dialog_msgs.dialog_id', $dialog_id);
|
||||
//
|
||||
if ($msg_id > 0) {
|
||||
$builder->whereReplyId($msg_id);
|
||||
$reDialog = false;
|
||||
}
|
||||
//
|
||||
if ($position_id > 0) {
|
||||
$array = $builder->clone()
|
||||
->where('web_socket_dialog_msgs.id', '>=', $position_id)
|
||||
@ -325,11 +334,12 @@ class DialogController extends AbstractController
|
||||
$cloner = $builder->clone();
|
||||
if ($prev_id > 0) {
|
||||
$cloner->where('web_socket_dialog_msgs.id', '<=', $prev_id)->orderByDesc('web_socket_dialog_msgs.id');
|
||||
$reDialog = false;
|
||||
} elseif ($next_id > 0) {
|
||||
$cloner->where('web_socket_dialog_msgs.id', '>=', $next_id)->orderBy('web_socket_dialog_msgs.id');
|
||||
$reDialog = false;
|
||||
} else {
|
||||
$cloner->orderByDesc('web_socket_dialog_msgs.id');
|
||||
$data['dialog'] = $dialog->formatData($user->userid);
|
||||
}
|
||||
$list = $cloner->take($take)->get()->sortByDesc('id', SORT_NUMERIC)->values();
|
||||
//
|
||||
@ -358,6 +368,9 @@ class DialogController extends AbstractController
|
||||
$isMarkDialogUser->save();
|
||||
}
|
||||
//
|
||||
if ($reDialog) {
|
||||
$data['dialog'] = $dialog->formatData($user->userid);
|
||||
}
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@
|
||||
|
||||
<!-- 发送按钮 -->
|
||||
<li class="chat-send" :class="sendClass" v-touchmouse="clickSend">
|
||||
<ETooltip placement="top" :disabled="windowSmall" :content="$L('发送')">
|
||||
<ETooltip placement="top" :disabled="windowSmall" :content="$L(sendClass === 'recorder' ? '长按录音' : '发送')">
|
||||
<div v-if="loading">
|
||||
<div class="chat-load">
|
||||
<Loading/>
|
||||
|
||||
@ -7,12 +7,14 @@
|
||||
:msg-data="source"
|
||||
:dialog-type="dialogData.type"
|
||||
:hide-percentage="hidePercentage"
|
||||
:hide-reply="hideReply"
|
||||
:operate-visible="operateVisible"
|
||||
:operate-action="operateVisible && source.id === operateItem.id"
|
||||
@on-longpress="onLongpress"
|
||||
@on-view-reply="onViewReply"
|
||||
@on-view-text="onViewText"
|
||||
@on-view-file="onViewFile"
|
||||
@on-reply-list="onReplyList"
|
||||
@on-emoji="onEmoji"/>
|
||||
</div>
|
||||
</template>
|
||||
@ -41,6 +43,14 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hideReply: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isReply: {
|
||||
type: Boolean, // 是否回复对象
|
||||
default: false
|
||||
},
|
||||
operateVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -65,7 +75,7 @@ export default {
|
||||
classArray() {
|
||||
return {
|
||||
'dialog-item': true,
|
||||
'self': this.source.userid == this.userId,
|
||||
'self': !this.isReply && this.source.userid == this.userId,
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -87,11 +97,20 @@ export default {
|
||||
this.dispatch("on-view-file", data)
|
||||
},
|
||||
|
||||
onReplyList(data) {
|
||||
this.dispatch("on-reply-list", data)
|
||||
},
|
||||
|
||||
onEmoji(data) {
|
||||
this.dispatch("on-emoji", data)
|
||||
},
|
||||
|
||||
dispatch(event, arg) {
|
||||
if (this.isReply) {
|
||||
this.$emit(event, arg)
|
||||
return
|
||||
}
|
||||
|
||||
let parent = this.$parent
|
||||
let name = parent.$options.name
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
:class="headClass"
|
||||
v-longpress="{callback: handleLongpress, delay: 300}">
|
||||
<!--回复-->
|
||||
<div v-if="msgData.reply_data" class="dialog-reply no-dark-content" @click="viewReply">
|
||||
<div v-if="!hideReply && msgData.reply_data" class="dialog-reply no-dark-content" @click="viewReply">
|
||||
<UserAvatar :userid="msgData.reply_data.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/>
|
||||
<div class="reply-desc">{{formatMsgDesc(msgData.reply_data)}}</div>
|
||||
</div>
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
<div class="dialog-foot">
|
||||
<!--回复数-->
|
||||
<div v-if="msgData.reply_num > 0" class="reply">
|
||||
<div v-if="!hideReply && msgData.reply_num > 0" class="reply" @click="replyList">
|
||||
<i class="taskfont"></i>
|
||||
{{msgData.reply_num}}条回复
|
||||
</div>
|
||||
@ -154,6 +154,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hideReply: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
operateVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -393,6 +397,12 @@ export default {
|
||||
this.$emit("on-view-file", this.msgData)
|
||||
},
|
||||
|
||||
replyList() {
|
||||
this.$emit("on-reply-list", {
|
||||
msg_id: this.msgData.id,
|
||||
})
|
||||
},
|
||||
|
||||
onEmoji(symbol) {
|
||||
this.$emit("on-emoji", {
|
||||
msg_id: this.msgData.id,
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
:data-component="msgItem"
|
||||
|
||||
:item-class-add="itemClassAdd"
|
||||
:extra-props="{dialogData, operateVisible, operateItem, hidePercentage: isMyDialog}"
|
||||
:extra-props="{dialogData, operateVisible, operateItem, hidePercentage: isMyDialog, hideReply: msgId > 0}"
|
||||
:estimate-size="78"
|
||||
:keeps="70"
|
||||
@scroll="onScroll"
|
||||
@ -87,6 +87,7 @@
|
||||
@on-view-reply="onViewReply"
|
||||
@on-view-text="onViewText"
|
||||
@on-view-file="onViewFile"
|
||||
@on-reply-list="onReplyList"
|
||||
@on-emoji="onEmoji">
|
||||
<template slot="header">
|
||||
<div v-if="loadMsg || prevId > 0" class="dialog-item loading"><Loading/></div>
|
||||
@ -110,7 +111,7 @@
|
||||
ref="input"
|
||||
v-model="msgText"
|
||||
:dialog-id="dialogId"
|
||||
:reply-id="replyId"
|
||||
:reply-id="replyActiveId"
|
||||
:emoji-bottom="windowSmall"
|
||||
:maxlength="200000"
|
||||
@on-focus="onEventFocus"
|
||||
@ -139,7 +140,7 @@
|
||||
<DropdownMenu slot="list">
|
||||
<DropdownItem name="action">
|
||||
<ul class="operate-action">
|
||||
<li @click="onOperate('reply')">
|
||||
<li v-if="msgId === 0" @click="onOperate('reply')">
|
||||
<i class="taskfont"></i>
|
||||
<span>{{ $L('回复') }}</span>
|
||||
</li>
|
||||
@ -252,6 +253,29 @@
|
||||
:size="400">
|
||||
<DialogGroupInfo v-if="groupInfoShow" :dialogId="dialogId"/>
|
||||
</DrawerOverlay>
|
||||
|
||||
<!--回复列表-->
|
||||
<DrawerOverlay
|
||||
v-model="replyListShow"
|
||||
placement="right"
|
||||
:size="500">
|
||||
<DialogWrapper
|
||||
v-if="replyListShow && replyListItem"
|
||||
:dialogId="dialogId"
|
||||
:msgId="replyListId"
|
||||
class="reply-list">
|
||||
<div slot="head" class="dialog-scroller">
|
||||
<DialogItem
|
||||
:source="replyListItem"
|
||||
@on-view-text="onViewText"
|
||||
@on-view-file="onViewFile"
|
||||
@on-emoji="onEmoji"
|
||||
hidePercentage
|
||||
hideReply
|
||||
isReply/>
|
||||
</div>
|
||||
</DialogWrapper>
|
||||
</DrawerOverlay>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -271,6 +295,7 @@ import {textImagesInfo} from "../../../functions/utils";
|
||||
export default {
|
||||
name: "DialogWrapper",
|
||||
components: {
|
||||
DialogItem,
|
||||
VirtualList,
|
||||
ChatInput,
|
||||
DialogGroupInfo,
|
||||
@ -284,6 +309,10 @@ export default {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
msgId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
autoFocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -333,9 +362,12 @@ export default {
|
||||
preventMoreLoad: false,
|
||||
preventToBottom: false,
|
||||
|
||||
replyId: 0,
|
||||
replyActiveId: 0,
|
||||
replyActiveIndex: -1,
|
||||
|
||||
replyListShow: false,
|
||||
replyListId: 0,
|
||||
|
||||
scrollDirection: null,
|
||||
scrollAction: 0,
|
||||
scrollTmp: 0,
|
||||
@ -372,8 +404,11 @@ export default {
|
||||
if (!this.isReady) {
|
||||
return [];
|
||||
}
|
||||
return this.dialogMsgs.filter(({dialog_id}) => {
|
||||
return dialog_id == this.dialogId;
|
||||
return this.dialogMsgs.filter(item => {
|
||||
if (this.msgId) {
|
||||
return item.reply_id == this.msgId;
|
||||
}
|
||||
return item.dialog_id == this.dialogId;
|
||||
}).sort((a, b) => {
|
||||
return a.id - b.id;
|
||||
});
|
||||
@ -383,8 +418,11 @@ export default {
|
||||
if (!this.isReady) {
|
||||
return [];
|
||||
}
|
||||
return this.tempMsgs.filter(({dialog_id}) => {
|
||||
return dialog_id == this.dialogId;
|
||||
return this.tempMsgs.filter(item => {
|
||||
if (this.msgId) {
|
||||
return item.reply_id == this.msgId;
|
||||
}
|
||||
return item.dialog_id == this.dialogId;
|
||||
});
|
||||
},
|
||||
|
||||
@ -400,7 +438,7 @@ export default {
|
||||
},
|
||||
|
||||
loadMsg() {
|
||||
return this.isLoad(`msg::${this.dialogId}-0`)
|
||||
return this.isLoad(`msg::${this.dialogId}-${this.msgId}`)
|
||||
},
|
||||
|
||||
prevId() {
|
||||
@ -467,6 +505,18 @@ export default {
|
||||
isMyDialog() {
|
||||
const {dialogData, userId} = this;
|
||||
return dialogData.dialog_user && dialogData.dialog_user.userid == userId
|
||||
},
|
||||
|
||||
replyId() {
|
||||
return parseInt(this.msgId > 0 ? this.msgId : this.replyActiveId)
|
||||
},
|
||||
|
||||
replyItem() {
|
||||
return this.replyId ? this.dialogMsgs.find(({id}) => id === this.replyId) : null
|
||||
},
|
||||
|
||||
replyListItem() {
|
||||
return this.replyListId ? this.dialogMsgs.find(item => item.id == this.replyListId) : null
|
||||
}
|
||||
},
|
||||
|
||||
@ -480,7 +530,10 @@ export default {
|
||||
this.allMsgs = this.allMsgList;
|
||||
requestAnimationFrame(this.onToBottom);
|
||||
}
|
||||
this.$store.dispatch("getDialogMsgs", {dialog_id}).then(_ => {
|
||||
this.$store.dispatch("getDialogMsgs", {
|
||||
dialog_id,
|
||||
msg_id: this.msgId
|
||||
}).then(_ => {
|
||||
this.openId = dialog_id;
|
||||
setTimeout(this.onSearchMsgId, 100)
|
||||
}).catch(_ => {});
|
||||
@ -523,7 +576,8 @@ export default {
|
||||
wsOpenNum(num) {
|
||||
if (num <= 1) return
|
||||
this.$store.dispatch("getDialogMsgs", {
|
||||
dialog_id: this.dialogId
|
||||
dialog_id: this.dialogId,
|
||||
msg_id: this.msgId,
|
||||
}).catch(_ => {});
|
||||
},
|
||||
|
||||
@ -594,7 +648,7 @@ export default {
|
||||
id: tempId,
|
||||
dialog_id: this.dialogData.id,
|
||||
reply_id: this.replyId,
|
||||
reply_data: this.replyId ? this.dialogMsgs.find(({id}) => id === this.replyId) : null,
|
||||
reply_data: this.replyItem,
|
||||
type: 'text',
|
||||
userid: this.userId,
|
||||
msg: {
|
||||
@ -612,7 +666,6 @@ export default {
|
||||
data: {
|
||||
dialog_id: this.dialogId,
|
||||
reply_id: this.replyId,
|
||||
reply_data: this.replyId ? this.dialogMsgs.find(({id}) => id === this.replyId) : null,
|
||||
text: msgText,
|
||||
},
|
||||
method: 'post'
|
||||
@ -638,7 +691,7 @@ export default {
|
||||
id: tempId,
|
||||
dialog_id: this.dialogData.id,
|
||||
reply_id: this.replyId,
|
||||
reply_data: this.replyId ? this.dialogMsgs.find(({id}) => id === this.replyId) : null,
|
||||
reply_data: this.replyItem,
|
||||
type: 'loading',
|
||||
userid: this.userId,
|
||||
msg,
|
||||
@ -718,6 +771,7 @@ export default {
|
||||
this.preventToBottom = true;
|
||||
this.$store.dispatch("getDialogMsgs", {
|
||||
dialog_id: this.dialogId,
|
||||
msg_id: this.msgId,
|
||||
position_id
|
||||
}).finally(_ => {
|
||||
const index = this.allMsgs.findIndex(item => item.id === position_id)
|
||||
@ -949,6 +1003,7 @@ export default {
|
||||
}
|
||||
this.$store.dispatch('getDialogMsgs', {
|
||||
dialog_id: this.dialogId,
|
||||
msg_id: this.msgId,
|
||||
prev_id: this.prevId
|
||||
}).then(({data}) => {
|
||||
const ids = data.list.map(item => item.id)
|
||||
@ -1048,8 +1103,9 @@ export default {
|
||||
if (nearMsg && nearMsg.id != rangeValue) {
|
||||
this.preventMoreLoad = true
|
||||
this.$store.dispatch("getDialogMsgs", {
|
||||
[key]: rangeValue,
|
||||
dialog_id: this.dialogId,
|
||||
msg_id: this.msgId,
|
||||
[key]: rangeValue,
|
||||
}).finally(_ => {
|
||||
this.preventMoreLoad = false
|
||||
})
|
||||
@ -1151,7 +1207,7 @@ export default {
|
||||
|
||||
onReply() {
|
||||
const {tail} = this.scrollInfo()
|
||||
this.replyId = this.operateItem.id
|
||||
this.replyActiveId = this.operateItem.id
|
||||
this.inputFocus()
|
||||
if (tail <= 10) {
|
||||
requestAnimationFrame(this.onToBottom)
|
||||
@ -1159,7 +1215,7 @@ export default {
|
||||
},
|
||||
|
||||
onCancelReply() {
|
||||
this.replyId = 0;
|
||||
this.replyActiveId = 0;
|
||||
},
|
||||
|
||||
onWithdraw() {
|
||||
@ -1307,6 +1363,14 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
onReplyList(data) {
|
||||
if (this.operateVisible) {
|
||||
return
|
||||
}
|
||||
this.replyListId = data.msg_id
|
||||
this.replyListShow = true
|
||||
},
|
||||
|
||||
onEmoji(data) {
|
||||
if (!$A.isJson(data)) {
|
||||
data = {
|
||||
|
||||
7
resources/assets/js/store/actions.js
vendored
7
resources/assets/js/store/actions.js
vendored
@ -2182,7 +2182,7 @@ export default {
|
||||
* @param state
|
||||
* @param dispatch
|
||||
* @param getters
|
||||
* @param data {dialog_id, ?reply_id, ?position_id, ?prev_id, ?next_id}
|
||||
* @param data {dialog_id, msg_id, ?position_id, ?prev_id, ?next_id}
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
getDialogMsgs({state, dispatch, getters}, data) {
|
||||
@ -2192,11 +2192,8 @@ export default {
|
||||
reject({msg: 'Parameter error'});
|
||||
return;
|
||||
}
|
||||
if (!/^d+$/.test(data.reply_id)) {
|
||||
data.reply_id = 0;
|
||||
}
|
||||
//
|
||||
const loadKey = `msg::${data.dialog_id}-${data.reply_id}`
|
||||
const loadKey = `msg::${data.dialog_id}-${data.msg_id}`
|
||||
if (getters.isLoad(loadKey)) {
|
||||
reject({msg: 'Loading'});
|
||||
return
|
||||
|
||||
@ -607,7 +607,6 @@
|
||||
}
|
||||
|
||||
.mention-item-name {
|
||||
flex-shrink: 0;
|
||||
padding: 0 8px;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
|
||||
@ -13,6 +13,29 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.reply-list {
|
||||
border-radius: 18px 0 0 18px;
|
||||
|
||||
.dialog-nav {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #f4f5f5;
|
||||
}
|
||||
|
||||
.dialog-scroller {
|
||||
padding: 16px 16px 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.vue-recycle-scroller.direction-vertical:not(.page-mode) {
|
||||
overflow-y: overlay;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user