diff --git a/resources/assets/js/pages/manage/components/DialogItem.vue b/resources/assets/js/pages/manage/components/DialogItem.vue index 053004a2f..911075fee 100644 --- a/resources/assets/js/pages/manage/components/DialogItem.vue +++ b/resources/assets/js/pages/manage/components/DialogItem.vue @@ -64,8 +64,6 @@ :hide-percentage="hidePercentage" :hide-reply="hideReply" :hide-forward="hideForward" - :operate-visible="operateVisible" - :operate-action="operateVisible && source.id === operateItem.id" :pointer-mouse="pointerMouse" :is-right-msg="isRightMsg" @on-view-reply="onViewReply" @@ -102,16 +100,6 @@ export default { return {} } }, - operateVisible: { - type: Boolean, - default: false - }, - operateItem: { - type: Object, - default() { - return {} - } - }, pointerMouse: { type: Boolean, default: false diff --git a/resources/assets/js/pages/manage/components/DialogView/index.vue b/resources/assets/js/pages/manage/components/DialogView/index.vue index 0d8eb9069..4f02c020f 100644 --- a/resources/assets/js/pages/manage/components/DialogView/index.vue +++ b/resources/assets/js/pages/manage/components/DialogView/index.vue @@ -257,6 +257,12 @@ export default { FileMsg, WCircle }, + inject: { + // 操作菜单状态(由 DialogWrapper 提供) + dialogOperate: { + default: () => ({visible: false, actionId: 0}) + }, + }, props: { msgData: { type: Object, @@ -280,14 +286,6 @@ export default { type: Boolean, default: false }, - operateVisible: { - type: Boolean, - default: false - }, - operateAction: { - type: Boolean, - default: false - }, pointerMouse: { type: Boolean, default: false @@ -301,6 +299,7 @@ export default { data() { return { timeShow: false, + operateAction: false, operateEnter: false, percentageLoad: 0, @@ -433,6 +432,13 @@ export default { }, watch: { + // 仅当前消息的操作状态变化时才更新 operateAction,避免打开菜单时所有消息重渲染 + 'dialogOperate.actionId': { + handler(id) { + this.operateAction = !!id && id === this.msgData.id + }, + immediate: true + }, operateAction(val) { this.operateEnter = false; if (val) { @@ -605,7 +611,7 @@ export default { }, playRecord() { - if (this.operateVisible) { + if (this.dialogOperate.visible) { return } if (!this.msgData.created_at) { @@ -615,7 +621,7 @@ export default { }, openMeeting() { - if (this.operateVisible) { + if (this.dialogOperate.visible) { return } emitter.emit('addMeeting', { diff --git a/resources/assets/js/pages/manage/components/DialogWrapper.vue b/resources/assets/js/pages/manage/components/DialogWrapper.vue index c09cfd131..5c9ea6e60 100644 --- a/resources/assets/js/pages/manage/components/DialogWrapper.vue +++ b/resources/assets/js/pages/manage/components/DialogWrapper.vue @@ -200,7 +200,7 @@ :data-sources="allMsgs" :data-component="msgItem" - :extra-props="{dialogData, operateVisible, operateItem, pointerMouse, isMyDialog, msgId, unreadOne, scrollIng, readEnabled, multiSelectMode, selectedMsgIdsSet}" + :extra-props="msgExtraProps" :estimate-size="dialogData.type=='group' ? 105 : 77" :keeps="dialogMsgKeep" :disabled="scrollDisabled" @@ -772,6 +772,12 @@ export default { }, directives: {touchclick, longpress, TransferDom, resizeObserver}, + provide() { + return { + dialogOperate: this.dialogOperate, + } + }, + props: { dialogId: { type: Number, @@ -862,6 +868,8 @@ export default { operateCopys: [], operateStyles: {}, operateItem: {}, + // 操作菜单状态(通过 provide 下发给消息项,避免作为 props 传递导致打开菜单时所有消息重渲染) + dialogOperate: {visible: false, actionId: 0}, multiSelectMode: false, selectedMsgIds: [], @@ -971,6 +979,11 @@ export default { }, computed: { + msgExtraProps() { + const {dialogData, pointerMouse, isMyDialog, msgId, unreadOne, scrollIng, readEnabled, multiSelectMode, selectedMsgIdsSet} = this; + return {dialogData, pointerMouse, isMyDialog, msgId, unreadOne, scrollIng, readEnabled, multiSelectMode, selectedMsgIdsSet} + }, + ...mapState([ 'systemConfig', 'userIsAdmin', @@ -1586,7 +1599,13 @@ export default { this.positionShow = true }, + 'operateItem.id'(id) { + this.dialogOperate.actionId = this.operateVisible ? id : 0 + }, + operateVisible(val) { + this.dialogOperate.visible = val + this.dialogOperate.actionId = val ? this.operateItem.id : 0 if (val || this.pointerMouse || this.focusLazy) { return } @@ -3307,67 +3326,68 @@ export default { this.onMultiSelectToggle(data.id); return; } - this.operateVisible = $A.isJson(data) && this.operateItem.id === data.id; - this.operateItem = $A.isJson(data) ? data : {}; - this.operateCopys = [] - if (event.target.nodeName === 'IMG') { - if (this.$Electron) { + // 菜单状态与位置在同一帧内更新,避免会话窗口连续重渲染两次 + requestAnimationFrame(() => { + this.operateVisible = $A.isJson(data) && this.operateItem.id === data.id; + this.operateItem = $A.isJson(data) ? data : {}; + this.operateCopys = [] + if (event.target.nodeName === 'IMG') { + if (this.$Electron) { + this.operateCopys.push({ + type: 'image', + icon: '', + label: '复制图片', + value: $A.thumbRestore(event.target.currentSrc), + }) + } + if (data.type !== 'file' && !isLocalHost(event.target.currentSrc)) { + this.operateCopys.push({ + type: 'imagedown', + icon: '', + label: '下载图片', + value: $A.thumbRestore(event.target.currentSrc), + }) + } + } else if (event.target.nodeName === 'A') { + if (event.target.classList.contains("mention") && event.target.classList.contains("file")) { + this.findOperateFile(this.operateItem.id, event.target.href) + } this.operateCopys.push({ - type: 'image', - icon: '', - label: '复制图片', - value: $A.thumbRestore(event.target.currentSrc), + type: 'link', + icon: '', + label: '复制链接', + value: event.target.href, }) } - if (data.type !== 'file' && !isLocalHost(event.target.currentSrc)) { - this.operateCopys.push({ - type: 'imagedown', - icon: '', - label: '下载图片', - value: $A.thumbRestore(event.target.currentSrc), - }) - } - } else if (event.target.nodeName === 'A') { - if (event.target.classList.contains("mention") && event.target.classList.contains("file")) { - this.findOperateFile(this.operateItem.id, event.target.href) - } this.operateCopys.push({ - type: 'link', - icon: '', - label: '复制链接', - value: event.target.href, + type: 'selected', + icon: '', + label: '复制选择', + value: '', + visible: false, }) - } - this.operateCopys.push({ - type: 'selected', - icon: '', - label: '复制选择', - value: '', - visible: false, - }) - if (data.type === 'text') { - if (data.msg.text.replace(/<[^>]+>/g,"").length > 0) { - this.operateCopys.push({ - type: 'text', - icon: '', - label: null, - title: this.operateCopys.length > 1 ? '复制文本' : '复制', - value: '', - }) + if (data.type === 'text') { + if (data.msg.text.replace(/<[^>]+>/g,"").length > 0) { + this.operateCopys.push({ + type: 'text', + icon: '', + label: null, + title: this.operateCopys.length > 1 ? '复制文本' : '复制', + value: '', + }) + } + if (data.msg.type === 'md') { + this.operateCopys.push({ + type: 'md', + icon: '', + label: '复制原文', + value: '', + }) + } } - if (data.msg.type === 'md') { - this.operateCopys.push({ - type: 'md', - icon: '', - label: '复制原文', - value: '', - }) + if (this.operateVisible) { + this.checkMessageFavoriteStatus(this.operateItem); } - } - if (this.operateVisible) { - this.checkMessageFavoriteStatus(this.operateItem); - } - requestAnimationFrame(() => { this.operateItem.clientX = event.clientX this.operateItem.clientY = event.clientY this.onSelectionchange()