perf: 优化对话详情页

This commit is contained in:
kuaifan 2022-06-19 00:15:31 +08:00
parent 09fd8aa1b0
commit b3c227d3cb
8 changed files with 118 additions and 137 deletions

View File

@ -218,7 +218,7 @@ class DialogController extends AbstractController
$user->task_dialog_id = $dialog->id;
$user->save();
}
//去掉标记未读
// 去掉标记未读
$isMarkDialogUser = WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($user->userid)->whereMarkUnread(1)->first();
if ($isMarkDialogUser) {
$isMarkDialogUser->mark_unread = 0;

View File

@ -27,6 +27,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property-read int|mixed $percentage
* @property-read \App\Models\WebSocketDialogMsg|null $reply_data
* @property-read \App\Models\WebSocketDialog|null $webSocketDialog
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newQuery()
@ -55,6 +56,7 @@ class WebSocketDialogMsg extends AbstractModel
protected $appends = [
'percentage',
'reply_data',
];
protected $hidden = [
@ -81,6 +83,21 @@ class WebSocketDialogMsg extends AbstractModel
return $this->appendattrs['percentage'];
}
/**
* 回复消息详情
* @return WebSocketDialogMsg|null
*/
public function getReplyDataAttribute()
{
if (!isset($this->appendattrs['reply_data'])) {
$this->appendattrs['reply_data'] = null;
if ($this->reply_id > 0) {
$this->appendattrs['reply_data'] = self::find($this->reply_id, ['id', 'userid', 'type', 'msg'])?->cancelAppend() ?: null;
}
}
return $this->appendattrs['reply_data'];
}
/**
* 消息格式化
* @param $value

View File

@ -54,7 +54,7 @@
"vue-resize-observer": "^2.0.16",
"vue-router": "^3.5.3",
"vue-template-compiler": "^2.6.14",
"vue-virtual-scroll-list": "^2.3.3",
"vue-virtual-scroll-list-hi": "^2.3.3-3",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.2",
"webpack": "^5.69.1",

View File

@ -277,7 +277,7 @@ export default {
}
},
computed: {
...mapState(['dialogInputCache', 'cacheProjects', 'cacheTasks', 'cacheUserBasic', 'dialogMsgs', 'dialogReplys']),
...mapState(['dialogInputCache', 'cacheProjects', 'cacheTasks', 'cacheUserBasic', 'dialogMsgs']),
isEnterSend() {
if (typeof this.enterSend === "boolean") {
@ -351,15 +351,7 @@ export default {
replyData() {
const {replyId} = this;
if (replyId > 0) {
let data = this.dialogMsgs.find(item => item.id === replyId)
if (data) {
return data;
}
data = this.dialogReplys.find(item => item.id === replyId)
if (data) {
return data;
}
this.$store.dispatch("getDialogReply", replyId)
return this.dialogMsgs.find(item => item.id === replyId)
}
return null;
}

View File

@ -10,9 +10,9 @@
:class="headClass"
v-longpress="{callback: handleLongpress, delay: 300}">
<!--回复-->
<div v-if="replyData" class="dialog-reply no-dark-content" @click="viewReply">
<UserAvatar :userid="replyData.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/>
<div class="reply-desc">{{formatMsgDesc(replyData)}}</div>
<div v-if="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>
<!--详情-->
<div class="dialog-content" :class="contentClass">
@ -172,7 +172,7 @@ export default {
},
computed: {
...mapState(['loads', 'dialogMsgs', 'dialogReplys', 'audioPlaying', 'windowActive']),
...mapState(['loads', 'audioPlaying', 'windowActive']),
isLoading() {
if (!this.msgData.created_at) {
@ -183,12 +183,12 @@ export default {
},
viewClass() {
const {msgData, replyData, operateAction, operateEnter} = this;
const {msgData, operateAction, operateEnter} = this;
const array = [];
if (msgData.type) {
array.push(msgData.type)
}
if (replyData) {
if (msgData.reply_data) {
array.push('reply-view')
}
if (operateAction) {
@ -237,22 +237,6 @@ export default {
}
}
return classArray;
},
replyData() {
const {reply_id} = this.msgData;
if (reply_id > 0) {
let data = this.dialogMsgs.find(item => item.id === reply_id)
if (data) {
return data;
}
data = this.dialogReplys.find(item => item.id === reply_id)
if (data) {
return data;
}
this.$store.dispatch("getDialogReply", reply_id)
}
return null;
}
},
@ -390,7 +374,7 @@ export default {
viewReply() {
this.$emit("on-view-reply", {
msg_id: this.msgData.id,
reply_id: this.replyData.id
reply_id: this.msgData.reply_id
})
},

View File

@ -81,6 +81,7 @@
:estimate-size="78"
:keeps="70"
@scroll="onScroll"
@range="onRange"
@totop="onNextPage"
@on-longpress="onLongpress"
@ -263,7 +264,7 @@ import DrawerOverlay from "../../../components/DrawerOverlay";
import DialogGroupInfo from "./DialogGroupInfo";
import ChatInput from "./ChatInput";
import VirtualList from 'vue-virtual-scroll-list'
import VirtualList from 'vue-virtual-scroll-list-hi'
import {Store} from "le5le-store";
import {textImagesInfo} from "../../../functions/utils";
@ -325,10 +326,11 @@ export default {
operateEmojis: ['👌', '🤝', '🤔', '👍', '👎', '👏', '✋', '✅', '❌', '❤️', '❓'],
recordState: '',
wrapperStart: 0,
wrapperStart: {},
scrollBalance: 0,
scrollMoreLoad: false,
scrollTail: 0,
preventMoreLoad: false,
preventToBottom: false,
replyId: 0,
replyActiveIndex: -1,
@ -434,7 +436,7 @@ export default {
if (this.msgNew > 0 && this.allMsgs.length > 0) {
return 'newmsg'
}
if (this.scrollBalance > 50) {
if (this.scrollTail > 50) {
return 'goto'
}
return null
@ -509,25 +511,27 @@ export default {
},
allMsgList(newList, oldList) {
const {balance} = this.scrollInfo();
const {tail} = this.scrollInfo();
this.allMsgs = newList;
//
if (!this.windowActive || (balance > 10 && oldList.length > 0)) {
if (!this.windowActive || (tail > 10 && oldList.length > 0)) {
const lastId = oldList[oldList.length - 1].id
const tmpList = newList.filter(item => item.id && item.id > lastId)
this.msgNew += tmpList.length
} else {
requestAnimationFrame(this.onToBottom)
if (!this.preventToBottom) {
requestAnimationFrame(this.onToBottom)
}
}
},
windowScrollY(val) {
if ($A.isIos()) {
const {balance} = this.scrollInfo();
const {tail} = this.scrollInfo();
this.navStyle = {
marginTop: val + 'px'
}
if (balance <= 10) {
if (tail <= 10) {
requestAnimationFrame(this.onToBottom)
}
}
@ -537,6 +541,12 @@ export default {
if (val) {
this.operateVisible = false;
}
},
replyActiveIndex(index) {
if (index > -1) {
setTimeout(_ => this.replyActiveIndex = -1, 800)
}
}
},
@ -711,7 +721,7 @@ export default {
}
if (this.wrapperStart.clientY > e.touches[0].clientY) {
//
if (this.wrapperStart.balance === 0) {
if (this.wrapperStart.tail === 0) {
e.preventDefault();
}
} else {
@ -796,8 +806,39 @@ export default {
onToBottom() {
this.msgNew = 0;
if (this.isReady) {
this.$refs.scroller?.scrollToBottom();
const scroller = this.$refs.scroller;
if (scroller) {
scroller.scrollToBottom();
requestAnimationFrame(_ => scroller.scrollToBottom()) //
}
},
onToIndex(index, addOffset) {
const scroller = this.$refs.scroller;
if (scroller) {
scroller.scrollToIndex(index, addOffset);
requestAnimationFrame(_ => scroller.scrollToIndex(index, addOffset)) //
}
},
onToOffset(offset) {
const scroller = this.$refs.scroller;
if (scroller) {
scroller.scrollToOffset(offset);
setTimeout(_ => scroller.scrollToOffset(offset), 10) //
}
},
scrollInfo() {
const scroller = this.$refs.scroller;
if (scroller) {
return scroller.scrollInfo();
} else {
return {
offset: 0,
scale: 0,
tail: 0
}
}
},
@ -838,15 +879,11 @@ export default {
const previousSize = typeof previousValue === "object" ? previousValue.size : scroller.getSize(previousValue)
return {size: previousSize + scroller.getSize(currentId)}
})
let size = this.$refs.scroller.getOffset() + offset.size;
let size = scroller.getOffset() + offset.size;
if (this.nextPage === 0) {
size -= 36
}
scroller.scrollToOffset(size);
setTimeout(_ => {
//
scroller.scrollToOffset(size);
}, 1)
this.onToOffset(size);
});
}).catch(() => {})
},
@ -906,52 +943,34 @@ export default {
}
},
scrollInfo() {
const scroller = this.$refs.scroller
if (!scroller) {
return {
scale: 0, //
offset: 0, //
balance: 0, //
}
}
let clientSize = scroller.getClientSize();
let offset = scroller.getOffset();
let scrollSize = scroller.getScrollSize();
return {
scale: offset / (scrollSize - clientSize),
offset: offset,
balance: scrollSize - clientSize - offset,
onScroll() {
this.operateVisible = false;
//
const {tail} = this.scrollInfo();
this.scrollTail = tail;
if (this.scrollTail <= 10) {
this.msgNew = 0;
}
},
onScroll(evt, range) {
this.operateVisible = false;
this.__onScroll && clearTimeout(this.__onScroll);
this.__onScroll = setTimeout(_ => {
const {balance} = this.scrollInfo();
this.scrollBalance = balance;
if (this.scrollBalance <= 10) {
this.msgNew = 0;
onRange(range) {
if (this.preventMoreLoad) {
return
}
let tmpPage = 0;
for (let i = range.start; i <= range.end; i++) {
if (tmpPage - parseInt(this.allMsgs[i]._page) > 1) {
this.preventMoreLoad = true
this.$store.dispatch("getDialogMoreMsgs", {
dialog_id: this.dialogId,
page: tmpPage - 1
}).finally(_ => {
this.preventMoreLoad = false
})
break;
}
//
if (!this.scrollMoreLoad) {
let tmpPage = 0;
for (let i = range.start; i <= range.end; i++) {
if (tmpPage - parseInt(this.allMsgs[i]._page) > 1) {
this.scrollMoreLoad = true
this.$store.dispatch("getDialogMoreMsgs", {
dialog_id: this.dialogId,
page: tmpPage - 1
}).finally(_ => {
this.scrollMoreLoad = false
})
break;
}
tmpPage = parseInt(this.allMsgs[i]._page);
}
}
}, 100)
tmpPage = parseInt(this.allMsgs[i]._page);
}
},
onBack() {
@ -1081,40 +1100,29 @@ export default {
if (this.operateVisible) {
return
}
const toIndex = (index) => {
this.$refs.scroller?.scrollToIndex(index)
requestAnimationFrame(_ => {
this.replyActiveIndex = index
setTimeout(_ => {
this.replyActiveIndex = -1
}, 800)
})
const runToIndex = (index) => {
this.onToIndex(index, -100)
requestAnimationFrame(_ => this.replyActiveIndex = index)
}
const index = this.allMsgs.findIndex(item => item.id === data.reply_id)
if (index > -1) {
toIndex(index)
runToIndex(index)
} else {
this.$store.dispatch("setLoad", {
key: `msg-${data.msg_id}`,
delay: 600
})
this.preventToBottom = true;
this.$store.dispatch("getDialogMoreMsgs", {
dialog_id: this.dialogId,
position_id: data.reply_id
}).then(_ => {
let i = 0;
let inter = setInterval(_ => {
i++
const index = this.allMsgs.findIndex(item => item.id === data.reply_id)
if (i > 10 || index > -1) {
clearInterval(inter)
if (index > -1) {
toIndex(index)
}
}
}, 100)
}).finally(_ => {
const index = this.allMsgs.findIndex(item => item.id === data.reply_id)
if (index > -1) {
runToIndex(index)
}
this.$store.dispatch("cancelLoad", `msg-${data.msg_id}`)
this.preventToBottom = false;
})
}
},

View File

@ -2151,25 +2151,6 @@ export default {
})
},
/**
* 获取回复消息
* @param state
* @param dispatch
* @param msg_id
*/
getDialogReply({state, dispatch}, msg_id) {
dispatch("call", {
url: 'dialog/msg/one',
data: {
msg_id: msg_id,
},
}).then(({data}) => {
state.dialogReplys.push(data)
}).catch(e => {
console.warn(e);
});
},
/**
* 获取会话消息
* @param state

View File

@ -72,7 +72,6 @@ const stateData = {
dialogId: 0,
dialogIns: [],
dialogMsgs: [],
dialogReplys: [],
dialogInputCache: $A.getStorageArray("cacheDialogInput"),
dialogMsgTransfer: {time: 0},