perf: 优化长按事件

This commit is contained in:
kuaifan 2025-04-12 18:29:39 +08:00
parent e3d0f571d2
commit aa74c5ccaf
6 changed files with 127 additions and 64 deletions

View File

@ -123,15 +123,15 @@
</ul> </ul>
</div> </div>
<div ref="menuProject" class="menu-project"> <div ref="menuProject" class="menu-project">
<ul> <ul v-longpress="handleLongpress">
<li <li
v-for="(item, key) in projectLists" v-for="(item, key) in projectLists"
:ref="`project_${item.id}`" :ref="`project_${item.id}`"
:key="key" :key="key"
:class="classNameProject(item)" :class="classNameProject(item)"
:data-id="item.id" :data-id="item.id"
@click="toggleRoute('project', {projectId: item.id})" @pointerdown="handleOperation"
v-longpress="handleLongpress"> @click="toggleRoute('project', {projectId: item.id})">
<div class="project-h1"> <div class="project-h1">
<em @click.stop="toggleOpenMenu(item.id)"></em> <em @click.stop="toggleOpenMenu(item.id)"></em>
<div class="title">{{item.name}}</div> <div class="title">{{item.name}}</div>
@ -505,16 +505,14 @@ export default {
'clientNewVersion', 'clientNewVersion',
'cacheTaskBrowse', 'cacheTaskBrowse',
'dialogIns',
'reportUnreadNumber', 'reportUnreadNumber',
'approveUnreadNumber', 'approveUnreadNumber',
'dialogIns',
'okrWindow', 'okrWindow',
'formOptions', 'formOptions',
'mobileTabbar',
'mobileTabbar' 'longpressData',
]), ]),
...mapGetters(['dashboardTask']), ...mapGetters(['dashboardTask']),
@ -1133,16 +1131,21 @@ export default {
this.workReportShow = true; this.workReportShow = true;
}, },
handleLongpress(event, el) { handleLongpress(event) {
const projectId = $A.getAttr(el, 'data-id') const {type, data, element} = this.longpressData;
const projectItem = this.projectLists.find(item => item.id == projectId) this.$store.commit("longpress/clear")
//
if (type !== 'manage') {
return
}
const projectItem = this.projectLists.find(item => item.id == data.projectId)
if (!projectItem) { if (!projectItem) {
return return
} }
this.operateVisible = false; this.operateVisible = false;
this.operateItem = $A.isJson(projectItem) ? projectItem : {}; this.operateItem = $A.isJson(projectItem) ? projectItem : {};
this.$nextTick(() => { this.$nextTick(() => {
const rect = el.getBoundingClientRect(); const rect = element.getBoundingClientRect();
const parentRect = this.$refs.boxMenu?.getBoundingClientRect() || {top: 0, left: 0} const parentRect = this.$refs.boxMenu?.getBoundingClientRect() || {top: 0, left: 0}
this.operateStyles = { this.operateStyles = {
left: `${event.clientX - parentRect.left}px`, left: `${event.clientX - parentRect.left}px`,
@ -1153,6 +1156,16 @@ export default {
}) })
}, },
handleOperation({currentTarget}) {
this.$store.commit("longpress/set", {
type: 'manage',
data: {
projectId: $A.getAttr(currentTarget, 'data-id')
},
element: currentTarget
})
},
handleTopClick() { handleTopClick() {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'project/top', url: 'project/top',

View File

@ -30,10 +30,8 @@
</div> </div>
<template v-else> <template v-else>
<div <div
ref="avatar"
class="dialog-avatar" class="dialog-avatar"
@contextmenu="handleOperation" @pointerdown="handleOperation">
@touchstart="handleOperation">
<UserAvatar :userid="source.userid" :size="30" @open-dialog="onOpenDialog"/> <UserAvatar :userid="source.userid" :size="30" @open-dialog="onOpenDialog"/>
</div> </div>
<DialogView <DialogView
@ -214,11 +212,11 @@ export default {
}) })
}, },
handleOperation() { handleOperation({currentTarget}) {
this.$store.commit("longpress/set", { this.$store.commit("longpress/set", {
type: 'mention', type: 'mention',
data: this.source, data: this.source,
element: this.$refs.avatar element: currentTarget
}) })
}, },

View File

@ -6,12 +6,10 @@
</div> </div>
<div <div
ref="dialogHead"
class="dialog-head" class="dialog-head"
:class="headClass" :class="headClass"
@click="handleClick" @click="handleClick"
@contextmenu="handleOperation" @pointerdown="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 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"> <div class="reply-avatar">
@ -394,11 +392,11 @@ export default {
}, },
methods: { methods: {
handleOperation() { handleOperation({currentTarget}) {
this.$store.commit("longpress/set", { this.$store.commit("longpress/set", {
type: 'operateMsg', type: 'operateMsg',
data: this.msgData, data: this.msgData,
element: this.$refs.dialogHead element: currentTarget
}) })
}, },

View File

@ -12,7 +12,10 @@
</Form> </Form>
</div> </div>
</div> </div>
<ul @touchstart="onTouchStart" @scroll="onScroll"> <ul
@scroll="onScroll"
@touchstart="onTouchStart"
v-longpress="handleLongpress">
<template v-if="projectLists.length === 0"> <template v-if="projectLists.length === 0">
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li> <li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
<li v-else class="nothing"> <li v-else class="nothing">
@ -24,7 +27,7 @@
:key="key" :key="key"
:data-id="item.id" :data-id="item.id"
:class="{operate: item.id == operateItem.id && operateVisible}" :class="{operate: item.id == operateItem.id && operateVisible}"
v-longpress="handleLongpress" @pointerdown="handleOperation"
@click="toggleRoute('project', {projectId: item.id})"> @click="toggleRoute('project', {projectId: item.id})">
<div class="project-item"> <div class="project-item">
<div class="item-left"> <div class="item-left">
@ -90,7 +93,7 @@ export default {
}, },
computed: { computed: {
...mapState(['cacheProjects', 'loadProjects']), ...mapState(['cacheProjects', 'loadProjects', 'longpressData']),
projectLists() { projectLists() {
const {projectKeyValue, cacheProjects} = this; const {projectKeyValue, cacheProjects} = this;
@ -173,16 +176,21 @@ export default {
}); });
}, },
handleLongpress(event, el) { handleLongpress(event) {
const projectId = $A.getAttr(el, 'data-id') const {type, data, element} = this.longpressData;
const projectItem = this.projectLists.find(item => item.id == projectId) this.$store.commit("longpress/clear")
//
if (type !== 'projectList') {
return
}
const projectItem = this.projectLists.find(item => item.id == data.projectId)
if (!projectItem) { if (!projectItem) {
return return
} }
this.operateVisible = false; this.operateVisible = false;
this.operateItem = $A.isJson(projectItem) ? projectItem : {}; this.operateItem = $A.isJson(projectItem) ? projectItem : {};
this.$nextTick(() => { this.$nextTick(() => {
const rect = el.getBoundingClientRect(); const rect = element.getBoundingClientRect();
const parentRect = this.$el.getBoundingClientRect() || {top: 0, left: 0} const parentRect = this.$el.getBoundingClientRect() || {top: 0, left: 0}
this.operateStyles = { this.operateStyles = {
left: `${event.clientX - parentRect.left}px`, left: `${event.clientX - parentRect.left}px`,
@ -193,6 +201,16 @@ export default {
}) })
}, },
handleOperation({currentTarget}) {
this.$store.commit("longpress/set", {
type: 'projectList',
data: {
projectId: $A.getAttr(currentTarget, 'data-id')
},
element: currentTarget
})
},
handleTopClick() { handleTopClick() {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'project/top', url: 'project/top',

View File

@ -119,7 +119,7 @@
<p>{{$L('没有任何文件')}}</p> <p>{{$L('没有任何文件')}}</p>
</div> </div>
<div v-else class="file-list" @contextmenu.prevent="handleContextmenu"> <div v-else class="file-list" @contextmenu.prevent="handleContextmenu">
<ul> <ul v-longpress="handleLongpress">
<li v-for="item in fileList"> <li v-for="item in fileList">
<div <div
class="file-item" class="file-item"
@ -129,7 +129,7 @@
operate: contextMenuVisible && item.id === contextMenuItem.id, operate: contextMenuVisible && item.id === contextMenuItem.id,
}" }"
:data-id="item.id" :data-id="item.id"
v-longpress="handleLongpress" @pointerdown="handleOperation"
@click="dropFile(item, 'openCheckMenu')"> @click="dropFile(item, 'openCheckMenu')">
<div class="file-check" :class="{'file-checked':selectIds.includes(item.id)}" @click.stop="dropFile(item, 'select')"> <div class="file-check" :class="{'file-checked':selectIds.includes(item.id)}" @click.stop="dropFile(item, 'select')">
<Checkbox :value="selectIds.includes(item.id)"/> <Checkbox :value="selectIds.includes(item.id)"/>
@ -769,7 +769,17 @@ export default {
}, },
computed: { computed: {
...mapState(['systemConfig', 'userIsAdmin', 'userInfo', 'fileLists', 'wsOpenNum', 'windowWidth', 'filePackLists', 'fileShakeId']), ...mapState([
'systemConfig',
'userIsAdmin',
'userInfo',
'fileLists',
'wsOpenNum',
'windowWidth',
'filePackLists',
'fileShakeId',
'longpressData'
]),
pid() { pid() {
const {folderId} = this.$route.params; const {folderId} = this.$route.params;
@ -1018,15 +1028,30 @@ export default {
this.autoBlur(id) this.autoBlur(id)
}, },
handleLongpress(event, el) { handleLongpress(event) {
const fileId = $A.getAttr(el, 'data-id') const {type, data} = this.longpressData;
const fileItem = this.fileList.find(item => item.id == fileId) this.$store.commit("longpress/clear")
//
if (type !== 'file') {
return
}
const fileItem = this.fileList.find(item => item.id == data.fileId)
if (!fileItem) { if (!fileItem) {
return return
} }
this.handleRightClick(event, fileItem) this.handleRightClick(event, fileItem)
}, },
handleOperation({currentTarget}) {
this.$store.commit("longpress/set", {
type: 'file',
data: {
fileId: $A.getAttr(currentTarget, 'data-id')
},
element: currentTarget
})
},
handleContextmenu(event) { handleContextmenu(event) {
if (this.windowLandscape) { if (this.windowLandscape) {
this.handleRightClick(event) this.handleRightClick(event)

View File

@ -68,7 +68,8 @@
class="messenger-list" class="messenger-list"
:hide-bar="operateVisible" :hide-bar="operateVisible"
@touchstart.native="listTouch" @touchstart.native="listTouch"
@on-scroll="listScroll"> @on-scroll="listScroll"
v-longpress="handleLongpress">
<ul v-if="tabActive==='dialog'" ref="ul" class="dialog"> <ul v-if="tabActive==='dialog'" ref="ul" class="dialog">
<template v-if="dialogList.length > 0"> <template v-if="dialogList.length > 0">
<li <li
@ -76,13 +77,14 @@
:ref="`dialog_${dialog.id}`" :ref="`dialog_${dialog.id}`"
:key="key" :key="key"
:data-id="dialog.id" :data-id="dialog.id"
data-type="dialog"
:class="dialogClass(dialog)" :class="dialogClass(dialog)"
@click="openDialog({ @click="openDialog({
dialog_id: dialog.id, dialog_id: dialog.id,
dialog_msg_id: dialog.search_msg_id, dialog_msg_id: dialog.search_msg_id,
search_msg_id: dialog.search_msg_id, search_msg_id: dialog.search_msg_id,
})" })"
v-longpress="handleDialogLongpress" @pointerdown="handleOperation"
:style="{'background-color':dialog.color}"> :style="{'background-color':dialog.color}">
<template v-if="dialog.type=='group'"> <template v-if="dialog.type=='group'">
<EAvatar v-if="dialog.avatar" class="img-avatar" :src="dialog.avatar" :size="42"></EAvatar> <EAvatar v-if="dialog.avatar" class="img-avatar" :src="dialog.avatar" :size="42"></EAvatar>
@ -141,9 +143,10 @@
v-for="(user, index) in items.list" v-for="(user, index) in items.list"
:key="index" :key="index"
:data-id="user.userid" :data-id="user.userid"
data-type="contacts"
:class="userClass(user)" :class="userClass(user)"
@click="openContacts(user)" @click="openContacts(user)"
v-longpress="handleUserLongpress"> @pointerdown="handleOperation">
<div class="avatar"><UserAvatar :userid="user.userid" :size="contactAvatarSize"/></div> <div class="avatar"><UserAvatar :userid="user.userid" :size="contactAvatarSize"/></div>
<div class="nickname"> <div class="nickname">
<em>{{user.nickname}}</em> <em>{{user.nickname}}</em>
@ -366,7 +369,8 @@ export default {
'dialogMsgs', 'dialogMsgs',
'messengerSearchKey', 'messengerSearchKey',
'appNotificationPermission', 'appNotificationPermission',
'taskColorList' 'taskColorList',
'longpressData'
]), ]),
...mapGetters(['getDialogDraft', 'tagDialogDraft']), ...mapGetters(['getDialogDraft', 'tagDialogDraft']),
@ -1031,38 +1035,34 @@ export default {
}) })
}, },
handleDialogLongpress(event, el) { handleLongpress(event) {
if (this.dialogSearchKey) { const {type, data, element} = this.longpressData;
return; this.$store.commit("longpress/clear")
} //
const dialogId = $A.getAttr(el, 'data-id') if (type !== 'messenger') {
const dialogItem = this.dialogList.find(item => item.id == dialogId)
if (!dialogItem) {
return return
} }
this.handleLongpress(dialogItem, el.getBoundingClientRect(), event.clientX)
},
handleUserLongpress(event, el) {
if (this.contactsKey) {
return;
}
const userId = $A.getAttr(el, 'data-id')
const userItem = this.contactsFilter.find(item => item.userid == userId)
if (!userItem) {
return
}
this.handleLongpress(userItem, el.getBoundingClientRect(), event.clientX)
},
handleLongpress(item, rect, clientX) {
this.operateType = this.tabActive; this.operateType = this.tabActive;
this.operateVisible = false; this.operateVisible = false;
this.operateItem = $A.isJson(item) ? item : {}; if (data.dataType === 'contacts') {
if (this.contactsKey) {
return;
}
this.operateItem = this.contactsFilter.find(item => item.userid == data.dataId)
} else {
if (this.dialogSearchKey) {
return;
}
this.operateItem = this.dialogList.find(item => item.id == data.dataId)
}
if (!this.operateItem) {
return
}
const rect = element.getBoundingClientRect();
this.$nextTick(() => { this.$nextTick(() => {
const parentRect = this.$refs.select?.getBoundingClientRect() || {top: 0, left: 0} const parentRect = this.$refs.select?.getBoundingClientRect() || {top: 0, left: 0}
this.operateStyles = { this.operateStyles = {
left: `${clientX}px`, left: `${event.clientX}px`,
top: `${rect.top + this.windowScrollY - parentRect.top}px`, top: `${rect.top + this.windowScrollY - parentRect.top}px`,
height: rect.height + 'px', height: rect.height + 'px',
} }
@ -1070,6 +1070,17 @@ export default {
}) })
}, },
handleOperation({currentTarget}) {
this.$store.commit("longpress/set", {
type: 'messenger',
data: {
dataId: $A.getAttr(currentTarget, 'data-id'),
dataType: $A.getAttr(currentTarget, 'data-type'),
},
element: currentTarget
})
},
handleDialogClick(act, value = undefined) { handleDialogClick(act, value = undefined) {
switch (act) { switch (act) {
case 'top': case 'top':