diff --git a/app/Http/Controllers/Api/DialogController.php b/app/Http/Controllers/Api/DialogController.php index b28c825fb..8c6322017 100755 --- a/app/Http/Controllers/Api/DialogController.php +++ b/app/Http/Controllers/Api/DialogController.php @@ -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; diff --git a/app/Models/WebSocketDialogMsg.php b/app/Models/WebSocketDialogMsg.php index 2c2013b95..d9b5236db 100644 --- a/app/Models/WebSocketDialogMsg.php +++ b/app/Models/WebSocketDialogMsg.php @@ -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 diff --git a/package.json b/package.json index 6b203d410..ab0071a6d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/resources/assets/js/pages/manage/components/ChatInput/index.vue b/resources/assets/js/pages/manage/components/ChatInput/index.vue index 991005620..f4f3cc0b0 100755 --- a/resources/assets/js/pages/manage/components/ChatInput/index.vue +++ b/resources/assets/js/pages/manage/components/ChatInput/index.vue @@ -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; } diff --git a/resources/assets/js/pages/manage/components/DialogView.vue b/resources/assets/js/pages/manage/components/DialogView.vue index 8668fd06b..cb84e0639 100644 --- a/resources/assets/js/pages/manage/components/DialogView.vue +++ b/resources/assets/js/pages/manage/components/DialogView.vue @@ -10,9 +10,9 @@ :class="headClass" v-longpress="{callback: handleLongpress, delay: 300}"> -
- -
{{formatMsgDesc(replyData)}}
+
+ +
{{formatMsgDesc(msgData.reply_data)}}
@@ -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 }) }, diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index e8434ed59..09a6183b6 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -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; }) } }, diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index 4908cc012..e62e286fc 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -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 diff --git a/resources/assets/js/store/state.js b/resources/assets/js/store/state.js index 015114b78..f5786c4dc 100644 --- a/resources/assets/js/store/state.js +++ b/resources/assets/js/store/state.js @@ -72,7 +72,6 @@ const stateData = { dialogId: 0, dialogIns: [], dialogMsgs: [], - dialogReplys: [], dialogInputCache: $A.getStorageArray("cacheDialogInput"), dialogMsgTransfer: {time: 0},