perf: 优化移动端选择交互

This commit is contained in:
kuaifan 2025-03-26 11:30:46 +08:00
parent 562697da27
commit f652f35c3a
23 changed files with 65 additions and 130 deletions

View File

@ -2,6 +2,7 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
-webkit-user-select: none;
user-select: none; user-select: none;
} }
@ -65,6 +66,7 @@ body {
box-shadow: none; box-shadow: none;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
-webkit-user-select: text; -webkit-user-select: text;
user-select: text;
} }
.map-box { .map-box {

View File

@ -79,9 +79,7 @@
margin: 0; margin: 0;
transition: 0.1s; transition: 0.1s;
font-weight: 500; font-weight: 500;
-moz-user-select: none; user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
padding: 8px 12px; padding: 8px 12px;
font-size: 14px; font-size: 14px;
border-radius: 4px; border-radius: 4px;

View File

@ -2,7 +2,7 @@
<div class="teditor-wrapper"> <div class="teditor-wrapper">
<div class="teditor-box" :class="[!inline && spinShow ? 'teditor-loadstyle' : 'teditor-loadedstyle']"> <div class="teditor-box" :class="[!inline && spinShow ? 'teditor-loadstyle' : 'teditor-loadedstyle']">
<template v-if="inline"> <template v-if="inline">
<div ref="myTextarea" :id="id" v-html="spinShow ? '' : content"></div> <div ref="myTextarea" class="user-select-auto" :id="id" v-html="spinShow ? '' : content"></div>
<Icon v-if="spinShow" type="ios-loading" :size="18" class="icon-loading icon-inline"></Icon> <Icon v-if="spinShow" type="ios-loading" :size="18" class="icon-loading icon-inline"></Icon>
</template> </template>
<template v-else> <template v-else>

View File

@ -36,7 +36,7 @@
<!-- 输入框 --> <!-- 输入框 -->
<div <div
ref="editor" ref="editor"
class="no-dark-content" class="no-dark-content user-select-auto"
@click.stop="onClickEditor" @click.stop="onClickEditor"
@paste="handlePaste"></div> @paste="handlePaste"></div>

View File

@ -11,7 +11,7 @@
@click="handleClick" @click="handleClick"
v-longpress="{callback: handleLongpress, delay: 300}"> v-longpress="{callback: handleLongpress, delay: 300}">
<!--回复--> <!--回复-->
<div v-if="!hideReply && msgData.reply_id && showReplyData(msgData.msg.reply_data)" class="dialog-reply no-dark-content" @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">
<UserAvatar :userid="msgData.msg.reply_data.userid" :show-icon="false" :show-name="true"/> <UserAvatar :userid="msgData.msg.reply_data.userid" :show-icon="false" :show-name="true"/>
</div> </div>
@ -297,24 +297,6 @@ export default {
return this.isLoad(`msg-${this.msgData.id}`) return this.isLoad(`msg-${this.msgData.id}`)
}, },
viewClass() {
const {msgData, operateAction, operateEnter, pointerMouse} = this;
const array = [];
if (msgData.type) {
array.push(msgData.type)
}
if (operateAction) {
array.push('operate-action')
if (operateEnter) {
array.push('pointer-mouse')
}
}
if (pointerMouse && array.indexOf('pointer-mouse') === -1) {
array.push('pointer-mouse')
}
return array
},
readList({userId}) { readList({userId}) {
return this.percentageList.filter(({userid, read_at}) => userid != userId && read_at) return this.percentageList.filter(({userid, read_at}) => userid != userId && read_at)
}, },
@ -331,26 +313,50 @@ export default {
return this.todoList.filter(({done_at}) => !done_at) return this.todoList.filter(({done_at}) => !done_at)
}, },
viewClass() {
const {msgData} = this;
const classArray = [];
if (msgData.type) {
classArray.push(msgData.type)
}
return classArray
},
headClass() { headClass() {
const {id, reply_id, type, msg, emoji, dot} = this.msgData; const {msgData, operateAction} = this;
const array = []; const {id, reply_id, type, msg, emoji, dot} = msgData;
const classArray = [];
if (operateAction) {
classArray.push('operating')
}
if (dot && !this.dotClicks.includes(id)) { if (dot && !this.dotClicks.includes(id)) {
array.push('dot') classArray.push('dot')
} }
if (reply_id === 0 && $A.arrayLength(emoji) === 0) { if (reply_id === 0 && $A.arrayLength(emoji) === 0) {
if (type === 'text') { if (type === 'text') {
if (/^<img\s+class="emoticon"[^>]*?>$/.test(msg.text) if (/^<img\s+class="emoticon"[^>]*?>$/.test(msg.text)
|| /^\s*<p>\s*([\uD800-\uDBFF][\uDC00-\uDFFF]){1,3}\s*<\/p>\s*$/.test(msg.text)) { || /^\s*<p>\s*([\uD800-\uDBFF][\uDC00-\uDFFF]){1,3}\s*<\/p>\s*$/.test(msg.text)) {
array.push('transparent') classArray.push('transparent')
} }
} }
} }
return array; return classArray;
},
replyClass() {
const classArray = [];
if (this.operateEnter || this.pointerMouse) {
classArray.push('user-select-auto')
}
return classArray;
}, },
contentClass() { contentClass() {
const {type, msg} = this.msgData; const {type, msg} = this.msgData;
const classArray = []; const classArray = [];
if (this.operateEnter || this.pointerMouse) {
classArray.push('user-select-auto')
}
if (type === 'text') { if (type === 'text') {
if (/^<img\s+class="emoticon"[^>]*?>$/.test(msg.text)) { if (/^<img\s+class="emoticon"[^>]*?>$/.test(msg.text)) {
classArray.push('an-emoticon') classArray.push('an-emoticon')

View File

@ -23,7 +23,7 @@
</div> </div>
<span slot="reference">[{{$L('未保存')}}*]</span> <span slot="reference">[{{$L('未保存')}}*]</span>
</EPopover> </EPopover>
{{fileName}} <span class="user-select-auto">{{fileName}}</span>
</div> </div>
<div class="header-user"> <div class="header-user">
<ul> <ul>
@ -75,7 +75,7 @@
<Button v-else :disabled="equalContent" :loading="loadSave > 0" class="header-button" size="small" type="primary" @click="handleClick('save')">{{$L('保存')}}</Button> <Button v-else :disabled="equalContent" :loading="loadSave > 0" class="header-button" size="small" type="primary" @click="handleClick('save')">{{$L('保存')}}</Button>
</template> </template>
</div> </div>
<div class="content-body"> <div class="content-body user-select-auto">
<div v-if="historyShow" class="content-mask"></div> <div v-if="historyShow" class="content-mask"></div>
<template v-if="file.type=='document'"> <template v-if="file.type=='document'">
<template v-if="contentDetail.type=='md'"> <template v-if="contentDetail.type=='md'">

View File

@ -4,7 +4,7 @@
<template v-else-if="contentDetail"> <template v-else-if="contentDetail">
<div v-show="headerShow && !['word', 'excel', 'ppt'].includes(file.type)" class="edit-header"> <div v-show="headerShow && !['word', 'excel', 'ppt'].includes(file.type)" class="edit-header">
<div class="header-title"> <div class="header-title">
<div class="title-name">{{$A.getFileName(file)}}</div> <div class="title-name user-select-auto">{{$A.getFileName(file)}}</div>
<Tag color="default">{{$L('只读')}}</Tag> <Tag color="default">{{$L('只读')}}</Tag>
<div class="refresh"> <div class="refresh">
<Loading v-if="contentLoad"/> <Loading v-if="contentLoad"/>
@ -12,7 +12,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="content-body"> <div class="content-body user-select-auto">
<template v-if="file.type=='document'"> <template v-if="file.type=='document'">
<VMPreview v-if="contentDetail.type=='md'" :value="contentDetail.content"/> <VMPreview v-if="contentDetail.type=='md'" :value="contentDetail.content"/>
<TEditor v-else :value="contentDetail.content" height="100%" readOnly/> <TEditor v-else :value="contentDetail.content" height="100%" readOnly/>

View File

@ -6,7 +6,7 @@
<div class="project-back" @click="onBack"> <div class="project-back" @click="onBack">
<i class="taskfont">&#xe676;</i> <i class="taskfont">&#xe676;</i>
</div> </div>
<h1 @click="showName">{{projectData.name}}</h1> <h1 @click="showName" class="user-select-auto">{{projectData.name}}</h1>
<div v-if="loading" class="project-load"><Loading/></div> <div v-if="loading" class="project-load"><Loading/></div>
</div> </div>
<ul class="project-icons"> <ul class="project-icons">
@ -76,7 +76,7 @@
</ul> </ul>
</div> </div>
<div class="project-subbox"> <div class="project-subbox">
<div class="project-subtitle" @click="showDesc"> <div class="project-subtitle user-select-auto" @click="showDesc">
<VMPreviewNostyle ref="descPreview" :value="projectData.desc"/> <VMPreviewNostyle ref="descPreview" :value="projectData.desc"/>
</div> </div>
<div class="project-switch"> <div class="project-switch">

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="report-detail"> <div class="report-detail">
<div class="report-title"> <div class="report-title user-select-auto">
{{ data.title }} {{ data.title }}
<Icon v-if="loadIng > 0" type="ios-loading" class="icon-loading"></Icon> <Icon v-if="loadIng > 0" type="ios-loading" class="icon-loading"></Icon>
</div> </div>
@ -40,7 +40,7 @@
</div> </div>
</li> </li>
</ul> </ul>
<div class="report-content" v-html="data.content"></div> <div class="report-content user-select-auto" v-html="data.content"></div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -74,7 +74,7 @@
<div v-if="taskDetail.archived_at" class="flow"> <div v-if="taskDetail.archived_at" class="flow">
<span class="archived" @click.stop="openMenu($event, taskDetail)">{{$L('已归档')}}</span> <span class="archived" @click.stop="openMenu($event, taskDetail)">{{$L('已归档')}}</span>
</div> </div>
<div class="nav"> <div class="nav user-select-auto">
<p v-if="projectName"><span>{{projectName}}</span></p> <p v-if="projectName"><span>{{projectName}}</span></p>
<p v-if="columnName"><span>{{columnName}}</span></p> <p v-if="columnName"><span>{{columnName}}</span></p>
<p v-if="taskDetail.id"><span>{{taskDetail.id}}</span></p> <p v-if="taskDetail.id"><span>{{taskDetail.id}}</span></p>

View File

@ -5,14 +5,14 @@
<div v-else-if="info" class="file-preview"> <div v-else-if="info" class="file-preview">
<div v-if="showHeader" class="edit-header"> <div v-if="showHeader" class="edit-header">
<div class="header-title"> <div class="header-title">
<div class="title-name">{{pageName}}</div> <div class="title-name user-select-auto">{{pageName}}</div>
<Tag color="default">{{$L('只读')}}</Tag> <Tag color="default">{{$L('只读')}}</Tag>
<div class="refresh"> <div class="refresh">
<Icon type="ios-refresh" @click="getInfo" /> <Icon type="ios-refresh" @click="getInfo" />
</div> </div>
</div> </div>
</div> </div>
<div class="content-body"> <div class="content-body user-select-auto">
<TEditor :value="info.content" height="100%" readOnly/> <TEditor :value="info.content" height="100%" readOnly/>
</div> </div>
</div> </div>

View File

@ -7,7 +7,6 @@
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: 99; z-index: 99;
user-select: none;
.common-network-exception { .common-network-exception {
position: fixed; position: fixed;
@ -323,14 +322,23 @@
} }
body.window-touch { body.window-touch {
user-select: none;
a, img {
-webkit-touch-callout: none;
}
.mobile-back { .mobile-back {
display: block; display: block;
} }
input, input,
textarea, textarea,
.ql-editor { .user-select-auto {
caret-color: $flow-status-end-color; caret-color: $flow-status-end-color;
user-select: auto;
a, img {
-webkit-touch-callout: default;
}
} }
} }

View File

@ -17,10 +17,6 @@ $--font-path: '../css/fonts';
@import "~element-sea/packages/theme-chalk/src/dropdown-item"; @import "~element-sea/packages/theme-chalk/src/dropdown-item";
@import "~element-sea/packages/theme-chalk/src/notification"; @import "~element-sea/packages/theme-chalk/src/notification";
.el-dropdown-menu {
user-select: none;
}
.el-dropdown-menu__item { .el-dropdown-menu__item {
min-width: 100px; min-width: 100px;
.item { .item {

View File

@ -102,7 +102,6 @@ body {
} }
.ivu-select-dropdown { .ivu-select-dropdown {
user-select: none;
max-height: 360px; max-height: 360px;
&.select-node { &.select-node {

View File

@ -245,7 +245,7 @@
color: #0bc037; color: #0bc037;
background-color: transparent; background-color: transparent;
margin-right: 0; margin-right: 0;
-webkit-user-select: auto; user-select: auto;
> span { > span {
margin: 0 2px; margin: 0 2px;
} }
@ -298,7 +298,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
user-select: none;
> li { > li {
width: 30px; width: 30px;
height: 30px; height: 30px;
@ -589,7 +588,6 @@
text-align: center; text-align: center;
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
user-select: none;
> img { > img {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
@ -1003,7 +1001,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
user-select: none;
margin: 0 -12px; margin: 0 -12px;
&.activation { &.activation {
> li { > li {

View File

@ -672,12 +672,6 @@
max-width: 70%; max-width: 70%;
} }
&.operate-action {
.dialog-head {
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
}
}
.dialog-username { .dialog-username {
max-width: 100%; max-width: 100%;
height: 22px; height: 22px;
@ -699,6 +693,10 @@
background-color: transparent !important; background-color: transparent !important;
} }
&.operating {
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
}
&.dot { &.dot {
position: relative; position: relative;
&:after { &:after {
@ -1940,7 +1938,6 @@
background-color: #ffffff; background-color: #ffffff;
border: 1px solid #eeeeee; border: 1px solid #eeeeee;
cursor: pointer; cursor: pointer;
user-select: none;
display: flex; display: flex;
align-content: center; align-content: center;
justify-content: center; justify-content: center;
@ -2435,7 +2432,7 @@ body:not(.window-touch) {
} }
} }
} }
.operate-action-top{ .operate-action-top {
width: auto; width: auto;
> li { > li {
margin-bottom: 0; margin-bottom: 0;
@ -2493,7 +2490,6 @@ body:not(.window-touch) {
body.window-touch { body.window-touch {
.dialog-wrapper { .dialog-wrapper {
.dialog-scroller { .dialog-scroller {
user-select: none;
.dialog-item { .dialog-item {
.dialog-view { .dialog-view {
.dialog-head { .dialog-head {
@ -2517,13 +2513,6 @@ body.window-touch {
} }
} }
} }
&.pointer-mouse {
.dialog-head {
.dialog-content {
user-select: text;
}
}
}
} }
} }
} }
@ -2666,15 +2655,13 @@ body.window-portrait {
.dialog-item { .dialog-item {
.dialog-view { .dialog-view {
&.text, &.text,
&.file,
&.location, &.location,
&.template { &.template {
max-width: calc(100% - 80px); max-width: calc(100% - 80px);
} }
.dialog-head { .dialog-head {
.dialog-content { .dialog-content {
a, img {
-webkit-touch-callout: none;
}
.content-text { .content-text {
> pre { > pre {
font-size: 15px; font-size: 15px;
@ -2694,15 +2681,6 @@ body.window-portrait {
} }
} }
} }
&.pointer-mouse {
.dialog-head {
.dialog-content {
a, img {
-webkit-touch-callout: default;
}
}
}
}
} }
} }
} }

View File

@ -65,7 +65,6 @@
width: 100%; width: 100%;
overflow: auto; overflow: auto;
background-color: #f8f8f8; background-color: #f8f8f8;
user-select: none;
> li { > li {
padding: 16px; padding: 16px;
margin: 16px; margin: 16px;

View File

@ -79,7 +79,6 @@ body.window-portrait {
} }
.calendar-box { .calendar-box {
padding: 0 24px 5px; padding: 0 24px 5px;
user-select: none;
.calendar-wrapper { .calendar-wrapper {
.tui-full-calendar-section-button { .tui-full-calendar-section-button {
> button { > button {

View File

@ -368,18 +368,6 @@
opacity: 0; opacity: 0;
} }
body.window-touch {
.page-messenger {
.messenger-wrapper {
.messenger-select {
.messenger-list {
user-select: none;
}
}
}
}
}
body.window-portrait { body.window-portrait {
.page-dashboard { .page-dashboard {
.dashboard-wrapper { .dashboard-wrapper {
@ -413,7 +401,6 @@ body.window-portrait {
} }
.dashboard-ul { .dashboard-ul {
margin-bottom: 20px; margin-bottom: 20px;
user-select: none;
&.ul-hidden { &.ul-hidden {
margin-bottom: 0; margin-bottom: 0;
} }

View File

@ -737,7 +737,6 @@ body.window-portrait {
} }
.file-drag { .file-drag {
.file-list { .file-list {
user-select: none;
> ul { > ul {
grid-template-columns: repeat(auto-fill, 80px); grid-template-columns: repeat(auto-fill, 80px);
grid-gap: 4px; grid-gap: 4px;
@ -778,15 +777,3 @@ body.window-portrait {
} }
} }
} }
body.window-touch {
.page-file {
.file-wrapper {
.file-drag {
.file-list {
user-select: none;
}
}
}
}
}

View File

@ -438,14 +438,6 @@
} }
} }
body.window-touch {
.page-manage {
.manage-box-menu {
user-select: none;
}
}
}
body.window-portrait { body.window-portrait {
.page-manage { .page-manage {
&.show-tabbar { &.show-tabbar {

View File

@ -241,7 +241,6 @@
padding: 0; padding: 0;
height: auto; height: auto;
width: auto; width: auto;
user-select: none;
} }
.bot { .bot {
color: $primary-color; color: $primary-color;
@ -658,18 +657,6 @@
} }
} }
body.window-touch {
.page-messenger {
.messenger-wrapper {
.messenger-select {
.messenger-list {
user-select: none;
}
}
}
}
}
body.window-portrait { body.window-portrait {
.page-messenger { .page-messenger {
.messenger-wrapper { .messenger-wrapper {
@ -710,7 +697,6 @@ body.window-portrait {
.messenger-list { .messenger-list {
ul { ul {
&.dialog { &.dialog {
user-select: none;
> li { > li {
.user-avatar { .user-avatar {
.common-avatar { .common-avatar {
@ -736,7 +722,6 @@ body.window-portrait {
} }
} }
&.contacts { &.contacts {
user-select: none;
> li { > li {
&.loaded { &.loaded {
height: 58px; height: 58px;

View File

@ -2,6 +2,7 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
-webkit-user-select: none;
user-select: none; user-select: none;
} }
@ -65,6 +66,7 @@ body {
box-shadow: none; box-shadow: none;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
-webkit-user-select: text; -webkit-user-select: text;
user-select: text;
} }
.map-box { .map-box {