perf: 优化长按操作

This commit is contained in:
kuaifan 2025-04-11 13:47:53 +08:00
parent bfdb72dd0a
commit 3cb9fff07f
6 changed files with 130 additions and 105 deletions

View File

@ -7,7 +7,7 @@ const longpress = {
mode = 'default',
isCall = false,
pressTimer = null,
callback = binding.value;
callback = binding.value; // 回调函数:第一个参数是事件对象(点到的对象),第二个参数是元素对象(注册绑定的对象)
if ($A.isJson(binding.value)) {
delay = binding.value.delay || 500;
callback = binding.value.callback;

View File

@ -29,12 +29,12 @@
{{source.msg.source === 'api' ? source.msg.notice : $L(source.msg.notice)}}
</div>
<template v-else>
<div class="dialog-avatar">
<UserAvatar
v-longpress="{callback: onMention, delay: 300}"
@open-dialog="onOpenDialog"
:userid="source.userid"
:size="30"/>
<div
ref="avatar"
class="dialog-avatar"
@contextmenu="handleOperation"
@touchstart="handleOperation">
<UserAvatar :userid="source.userid" :size="30" @open-dialog="onOpenDialog"/>
</div>
<DialogView
:msg-data="source"
@ -46,7 +46,6 @@
:operate-action="operateVisible && source.id === operateItem.id"
:pointer-mouse="pointerMouse"
:is-right-msg="isRightMsg"
@on-longpress="onLongpress"
@on-view-reply="onViewReply"
@on-view-text="onViewText"
@on-view-file="onViewFile"
@ -63,12 +62,10 @@
<script>
import {mapState} from "vuex";
import DialogView from "./DialogView";
import longpress from "../../../directives/longpress";
export default {
name: "DialogItem",
components: {DialogView},
directives: {longpress},
props: {
source: {
type: Object,
@ -217,6 +214,14 @@ export default {
})
},
handleOperation() {
this.$store.commit("longpress/set", {
type: 'mention',
data: this.source,
element: this.$refs.avatar
})
},
onOpenDialog(userid) {
if (this.dialogData.type == 'group' || ![this.dialogData.dialog_user?.userid, this.userId].includes(userid)) {
this.$store.dispatch("openDialogUserid", userid).catch(({msg}) => {
@ -225,14 +230,6 @@ export default {
}
},
onMention() {
this.dispatch("on-mention", this.source)
},
onLongpress(e) {
this.dispatch("on-longpress", e)
},
onViewReply(data) {
this.dispatch("on-view-reply", data)
},

View File

@ -6,10 +6,12 @@
</div>
<div
ref="dialogHead"
class="dialog-head"
:class="headClass"
@click="handleClick"
v-longpress="{callback: handleLongpress, delay: 300}">
@contextmenu="handleOperation"
@touchstart="handleOperation">
<!--回复-->
<div v-if="!hideReply && msgData.reply_id && showReplyData(msgData.msg.reply_data)" class="dialog-reply no-dark-content" :class="replyClass" @click="viewReply">
<div class="reply-avatar">
@ -178,7 +180,6 @@
<script>
import WCircle from "../../../../components/WCircle";
import {mapGetters, mapState} from "vuex";
import longpress from "../../../../directives/longpress";
import TextMsg from "./text.vue";
import LongTextMsg from "./longtext.vue";
@ -209,7 +210,6 @@ export default {
FileMsg,
WCircle
},
directives: {longpress},
props: {
msgData: {
type: Object,
@ -394,8 +394,12 @@ export default {
},
methods: {
handleLongpress(event, el) {
this.$emit("on-longpress", {event, el, msgData: this.msgData})
handleOperation() {
this.$store.commit("longpress/set", {
type: 'operateMsg',
data: this.msgData,
element: this.$refs.dialogHead
})
},
handleClick() {

View File

@ -182,7 +182,10 @@
</div>
<!--消息部分-->
<div ref="msgs" class="dialog-msgs">
<div
ref="msgs"
class="dialog-msgs"
v-longpress="{callback: handleLongpress, delay: 300}">
<!--定位提示-->
<div v-if="positionShow && positionMsg" class="dialog-position">
<div class="position-label" @click="onPositionMark(positionMsg.msg_id)">
@ -211,8 +214,6 @@
@range="onRange"
@visible="onVisible"
@on-mention="onMention"
@on-longpress="onLongpress"
@on-view-reply="onViewReply"
@on-view-text="onViewText"
@on-view-file="onViewFile"
@ -653,6 +654,7 @@ import DialogGroupWordChain from "./DialogGroupWordChain";
import DialogGroupVote from "./DialogGroupVote";
import DialogComplaint from "./DialogComplaint";
import touchclick from "../../../directives/touchclick";
import longpress from "../../../directives/longpress";
import {languageList} from "../../../language";
import {isLocalResourcePath} from "../../../components/Replace/utils";
import emitter from "../../../store/events";
@ -678,7 +680,7 @@ export default {
DialogGroupVote,
DialogComplaint,
},
directives: {touchclick},
directives: {touchclick, longpress},
props: {
dialogId: {
@ -875,6 +877,7 @@ export default {
'readLoadNum',
'readTimeout',
'formOptions',
'longpressData',
'cacheTranslationLanguage'
]),
@ -3003,6 +3006,92 @@ export default {
}
},
handleLongpress(event) {
const {type, data, element} = this.longpressData;
this.$store.commit("longpress/clear")
//
switch (type) {
//
case "mention":
const user = this.cacheUserBasic.find(({userid}) => userid == data.userid);
if (user) {
this.$refs.input?.addMention({
denotationChar: "@",
id: user.userid,
value: user.nickname,
})
}
break;
//
case "operateMsg":
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: '&#xe7cd;',
label: '复制图片',
value: $A.thumbRestore(event.target.currentSrc),
})
}
if (data.type !== 'file' && !isLocalResourcePath(event.target.currentSrc)) {
this.operateCopys.push({
type: 'imagedown',
icon: '&#xe7a8;',
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: '&#xe7cb;',
label: '复制链接',
value: event.target.href,
})
}
this.operateCopys.push({
type: 'selected',
icon: '&#xe7df;',
label: '复制选择',
value: '',
visible: false,
})
if (data.type === 'text') {
if (data.msg.text.replace(/<[^>]+>/g,"").length > 0) {
this.operateCopys.push({
type: 'text',
icon: '&#xe77f;',
label: null,
title: this.operateCopys.length > 1 ? '复制文本' : '复制',
value: '',
})
}
if (data.msg.type === 'md') {
this.operateCopys.push({
type: 'md',
icon: '&#xe77f;',
label: '复制原文',
value: '',
})
}
}
this.$nextTick(() => {
this.operateItem.clientX = event.clientX
this.operateItem.clientY = event.clientY
this.onSelectionchange()
this.onUpdateOperate(element)
})
break;
}
},
onMsgType(type) {
switch (type) {
case 'project':
@ -3027,83 +3116,6 @@ export default {
}
},
onMention(data) {
const user = this.cacheUserBasic.find(({userid}) => userid == data.userid);
if (user) {
this.$refs.input?.addMention({
denotationChar: "@",
id: user.userid,
value: user.nickname,
})
}
},
onLongpress({event, el, msgData}) {
this.operateVisible = this.operateItem.id === msgData.id;
this.operateItem = $A.isJson(msgData) ? msgData : {};
this.operateCopys = []
if (event.target.nodeName === 'IMG') {
if (this.$Electron) {
this.operateCopys.push({
type: 'image',
icon: '&#xe7cd;',
label: '复制图片',
value: $A.thumbRestore(event.target.currentSrc),
})
}
if (msgData.type !== 'file' && !isLocalResourcePath(event.target.currentSrc)) {
this.operateCopys.push({
type: 'imagedown',
icon: '&#xe7a8;',
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: '&#xe7cb;',
label: '复制链接',
value: event.target.href,
})
}
this.operateCopys.push({
type: 'selected',
icon: '&#xe7df;',
label: '复制选择',
value: '',
visible: false,
})
if (msgData.type === 'text') {
if (msgData.msg.text.replace(/<[^>]+>/g,"").length > 0) {
this.operateCopys.push({
type: 'text',
icon: '&#xe77f;',
label: null,
title: this.operateCopys.length > 1 ? '复制文本' : '复制',
value: '',
})
}
if (msgData.msg.type === 'md') {
this.operateCopys.push({
type: 'md',
icon: '&#xe77f;',
label: '复制原文',
value: '',
})
}
}
this.$nextTick(() => {
this.operateItem.clientX = event.clientX
this.operateItem.clientY = event.clientY
this.onSelectionchange()
this.onUpdateOperate(el)
})
},
onSelectionchange() {
if (!this.operateVisible) {
return

View File

@ -284,4 +284,13 @@ export default {
$A.IDBSave("dialogQuotes", state.dialogQuotes)
}
},
// 长按事件
'longpress/set': function(state, {type, data, element}) {
state.longpressData = {type, data, element}
},
'longpress/clear': function(state) {
state.longpressData = {type: '', data: null, element: null}
},
}

View File

@ -261,5 +261,8 @@ export default {
cacheTranscriptionLanguage: '',
// 下拉菜单操作
menuOperation: {}
menuOperation: {},
// 长按数据
longpressData: {type: '', data: null, element: null},
};