perf: 优化搜索加载提示

This commit is contained in:
kuaifan 2022-05-25 14:36:21 +08:00
parent c5a0e04242
commit 0911bc2dc9
7 changed files with 165 additions and 68 deletions

View File

@ -3,15 +3,21 @@
<PageTitle :title="$L('项目')"/> <PageTitle :title="$L('项目')"/>
<div class="list-search"> <div class="list-search">
<div class="search-wrapper"> <div class="search-wrapper">
<Input v-model="projectKeyValue" :placeholder="$L(loadProjects || projectKeyLoading ? '读取中...' : '搜索...')" clearable> <Input v-model="projectKeyValue" :placeholder="$L(loadProjects ? '更新中...' : '搜索项目')" clearable>
<div class="search-pre" slot="prefix"> <div class="search-pre" slot="prefix">
<Loading v-if="loadProjects || projectKeyLoading"/> <Loading v-if="loadProjects"/>
<Icon v-else type="ios-search" /> <Icon v-else type="ios-search" />
</div> </div>
</Input> </Input>
</div> </div>
</div> </div>
<ul> <ul>
<template v-if="projectLists.length === 0">
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
<li v-else class="nothing">
{{$L(projectLists ? `没有任何与"${projectKeyValue}"相关的项目` : `没有任何项目`)}}
</li>
</template>
<li <li
v-for="(item, key) in projectLists" v-for="(item, key) in projectLists"
:key="key" :key="key"
@ -49,7 +55,6 @@ export default {
data() { data() {
return { return {
projectKeyValue: '', projectKeyValue: '',
projectKeyAlready: {},
projectKeyLoading: 0, projectKeyLoading: 0,
} }
}, },
@ -89,31 +94,25 @@ export default {
if (val == '') { if (val == '') {
return; return;
} }
this.projectKeyLoading++;
setTimeout(() => { setTimeout(() => {
if (this.projectKeyValue == val) { if (this.projectKeyValue == val) {
this.searchProject(); this.searchProject();
} }
this.projectKeyLoading--;
}, 600); }, 600);
}, },
}, },
methods: { methods: {
searchProject() { searchProject() {
if (this.projectKeyAlready[this.projectKeyValue] === true) { this.projectKeyLoading++;
return;
}
this.projectKeyAlready[this.projectKeyValue] = true;
//
setTimeout(() => {
this.projectKeyLoading++;
}, 1000)
this.$store.dispatch("getProjects", { this.$store.dispatch("getProjects", {
keys: { keys: {
name: this.projectKeyValue name: this.projectKeyValue
} },
}).then(() => { hideLoad: true,
this.projectKeyLoading--; }).finally(_ => {
}).catch(() => {
this.projectKeyLoading--; this.projectKeyLoading--;
}); });
}, },

View File

@ -5,7 +5,9 @@
<div class="dashboard-hello">{{$L('欢迎您,' + userInfo.nickname)}}</div> <div class="dashboard-hello">{{$L('欢迎您,' + userInfo.nickname)}}</div>
<div class="dashboard-desc"> <div class="dashboard-desc">
{{$L('以下是你当前的任务统计数据')}} {{$L('以下是你当前的任务统计数据')}}
<div v-if="loadDashboardTasks" class="dashboard-load"><Loading/></div> <transition name="dashboard-load">
<div v-if="loadDashboardTasks" class="dashboard-load"><Loading/></div>
</transition>
</div> </div>
<ul class="dashboard-block"> <ul class="dashboard-block">
<li @click="scrollTo('today')"> <li @click="scrollTo('today')">

View File

@ -5,16 +5,16 @@
<div class="messenger-select"> <div class="messenger-select">
<div class="messenger-search"> <div class="messenger-search">
<div class="search-wrapper"> <div class="search-wrapper">
<Input v-if="tabActive==='dialog'" v-model="dialogKey" :placeholder="$L(loadDialogs ? '读取中...' : '搜索...')" clearable > <Input v-if="tabActive==='dialog'" v-model="dialogKey" :placeholder="$L(loadDialogs ? '更新中...' : '搜索消息')" clearable >
<div class="search-pre" slot="prefix"> <div class="search-pre" slot="prefix">
<Loading v-if="loadDialogs"/> <Loading v-if="loadDialogs"/>
<Icon v-else type="ios-search" /> <Icon v-else type="ios-search" />
</div> </div>
</Input> </Input>
<Input v-else prefix="ios-search" v-model="contactsKey" :placeholder="$L('搜索...')" clearable /> <Input v-else prefix="ios-search" v-model="contactsKey" :placeholder="$L('搜索联系人')" clearable />
</div> </div>
</div> </div>
<div v-if="tabActive==='dialog'" class="messenger-nav"> <div v-if="tabActive==='dialog' && !dialogKey" class="messenger-nav">
<p <p
v-for="(item, key) in dialogType" v-for="(item, key) in dialogType"
:key="key" :key="key"
@ -34,7 +34,11 @@
v-if="tabActive==='dialog'" v-if="tabActive==='dialog'"
ref="dialogWrapper" ref="dialogWrapper"
class="dialog" > class="dialog" >
<li v-if="dialogList.length === 0" class="nothing">
{{$L(dialogKey ? `没有任何与"${dialogKey}"相关的对话` : `没有任何对话`)}}
</li>
<li <li
v-else
v-for="(dialog, key) in dialogList" v-for="(dialog, key) in dialogList"
:ref="`dialog_${dialog.id}`" :ref="`dialog_${dialog.id}`"
:key="key" :key="key"
@ -76,17 +80,24 @@
</li> </li>
</ul> </ul>
<ul v-else class="contacts"> <ul v-else class="contacts">
<li v-for="(users, label) in contactsData"> <template v-if="contactsFilter.length === 0">
<div class="label">{{label}}</div> <li v-if="contactsLoad > 0" class="loading"><Loading/></li>
<ul> <li v-else class="nothing">
<li v-for="(user, index) in users" :key="index" @click="openContacts(user)"> {{$L(contactsKey ? `没有任何与"${contactsKey}"相关的联系人` : `没有任何联系人`)}}
<div class="avatar"><UserAvatar :userid="user.userid" :size="30"/></div> </li>
<div class="nickname">{{user.nickname}}</div> </template>
</li> <template v-else>
</ul> <li v-for="items in contactsList">
</li> <div class="label">{{items.az}}</div>
<li v-if="contactsLoad > 0" class="loading"><Loading/></li> <ul>
<li v-else-if="!contactsHasMorePages" class="loaded">{{$L('共' + contactsList.length + '位联系人')}}</li> <li v-for="(user, index) in items.list" :key="index" @click="openContacts(user)">
<div class="avatar"><UserAvatar :userid="user.userid" :size="30"/></div>
<div class="nickname">{{user.nickname}}</div>
</li>
</ul>
</li>
<li class="loaded">{{$L('共' + contactsFilter.length + '位联系人')}}</li>
</template>
</ul> </ul>
<div class="top-operate" :style="topOperateStyles"> <div class="top-operate" :style="topOperateStyles">
<Dropdown <Dropdown
@ -156,7 +167,6 @@ export default {
contactsKey: '', contactsKey: '',
contactsLoad: 0, contactsLoad: 0,
contactsList: [],
contactsData: null, contactsData: null,
contactsCurrentPage: 1, contactsCurrentPage: 1,
contactsHasMorePages: false, contactsHasMorePages: false,
@ -200,7 +210,13 @@ export default {
if (!this.filterDialog(dialog)) { if (!this.filterDialog(dialog)) {
return false; return false;
} }
if (dialogActive) { if (dialogKey) {
let existName = $A.strExists(dialog.name, dialogKey);
let existMsg = dialog.last_msg && dialog.last_msg.type === 'text' && $A.strExists(dialog.last_msg.msg.text, dialogKey);
if (!existName && !existMsg) {
return false;
}
} else if (dialogActive) {
switch (dialogActive) { switch (dialogActive) {
case 'project': case 'project':
case 'task': case 'task':
@ -217,13 +233,6 @@ export default {
return false; return false;
} }
} }
if (dialogKey) {
let existName = $A.strExists(dialog.name, dialogKey);
let existMsg = dialog.last_msg && dialog.last_msg.type === 'text' && $A.strExists(dialog.last_msg.msg.text, dialogKey);
if (!existName && !existMsg) {
return false;
}
}
return true; return true;
}).sort((a, b) => { }).sort((a, b) => {
if (a.top_at || b.top_at) { if (a.top_at || b.top_at) {
@ -233,6 +242,36 @@ export default {
}) })
}, },
contactsFilter() {
const {contactsData, contactsKey} = this;
if (contactsData === null) {
return [];
}
if (contactsKey) {
return contactsData.filter(item => $A.strExists(item.email, contactsKey) || $A.strExists(item.nickname, contactsKey))
}
return contactsData;
},
contactsList() {
let list = [];
this.contactsFilter.some(user => {
let az = user.az ? user.az.toUpperCase() : "#";
let item = list.find(item => item.az = az);
if (item) {
if (item.list.findIndex(({userid}) => userid == user.userid) === -1) {
item.list.push(user)
}
} else {
list.push({
az,
list: [user]
})
}
})
return list;
},
msgUnread() { msgUnread() {
return function (type) { return function (type) {
let num = 0; let num = 0;
@ -285,11 +324,15 @@ export default {
} }
}, },
contactsKey(val) { contactsKey(val) {
if (val == '') {
return;
}
this.contactsLoad++;
setTimeout(() => { setTimeout(() => {
if (this.contactsKey == val) { if (this.contactsKey == val) {
this.contactsData = null;
this.getContactsList(1); this.getContactsList(1);
} }
this.contactsLoad--;
}, 600); }, 600);
}, },
tabActive: { tabActive: {
@ -399,9 +442,6 @@ export default {
}, },
getContactsList(page) { getContactsList(page) {
if (this.contactsData === null) {
this.contactsData = {};
}
this.contactsLoad++; this.contactsLoad++;
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'users/search', url: 'users/search',
@ -416,19 +456,15 @@ export default {
pagesize: 50 pagesize: 50
}, },
}).then(({data}) => { }).then(({data}) => {
if (this.contactsData === null) {
this.contactsData = [];
}
data.data.some((user) => { data.data.some((user) => {
if (user.userid === this.userId) { if (user.userid === this.userId) {
return false; return false;
} }
let az = user.az ? user.az.toUpperCase() : "#"; if (this.contactsData.findIndex(item => item.userid = user.userid) === -1) {
if (typeof this.contactsData[az] === "undefined") this.contactsData[az] = []; this.contactsData.push(user)
//
let index = this.contactsData[az].findIndex(({userid}) => userid === user.userid);
if (index > -1) {
this.contactsData[az].splice(index, 1, user);
} else {
this.contactsData[az].push(user);
this.contactsList.push(user);
} }
}); });
this.contactsCurrentPage = data.current_page; this.contactsCurrentPage = data.current_page;

View File

@ -711,7 +711,12 @@ export default {
reject({msg: 'Parameter error'}); reject({msg: 'Parameter error'});
return; return;
} }
state.loadProjects++; let showLoad = true;
if (data.hideLoad !== undefined) {
showLoad = !data.hideLoad;
delete data.hideLoad;
}
showLoad && state.loadProjects++;
dispatch("call", { dispatch("call", {
url: 'project/lists', url: 'project/lists',
data: data || {} data: data || {}
@ -723,7 +728,7 @@ export default {
console.warn(e); console.warn(e);
reject(e) reject(e)
}).finally(_ => { }).finally(_ => {
state.loadProjects--; showLoad && state.loadProjects--;
}); });
}); });
}, },

View File

@ -24,14 +24,14 @@
overflow: hidden; overflow: hidden;
.search-pre { .search-pre {
width: 32px; width: 100%;
height: 32px; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.common-loading { .common-loading {
width: 16px; width: 14px;
height: 16px; height: 14px;
margin: 0; margin: 0;
} }
} }
@ -60,6 +60,25 @@
border-radius: 12px; border-radius: 12px;
background-color: #ffffff; background-color: #ffffff;
list-style: none; list-style: none;
&.nothing,
&.loading {
text-align: center;
height: 100%;
margin: 0;
padding: 24px;
border-radius: 0;
line-height: 22px;
}
&.loading {
display: flex;
align-items: flex-start;
justify-content: center;
.common-loading {
width: 20px;
height: 20px;
margin: 1px;
}
}
.project-h1 { .project-h1 {
font-size: 16px; font-size: 16px;
line-height: 22px; line-height: 22px;

View File

@ -261,6 +261,22 @@
} }
} }
// 加载状态延时显示立即隐藏
.dashboard-load-enter-active {
transition: opacity 0.3s ease;
transition-delay: 1s;
}
.dashboard-load-leave-active {
transition: opacity 0.2s ease;
transition-delay: 0s;
}
.dashboard-load-enter,
.dashboard-load-leave-to {
opacity: 0;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.page-dashboard { .page-dashboard {
.dashboard-wrapper { .dashboard-wrapper {

View File

@ -37,14 +37,14 @@
border-radius: 12px; border-radius: 12px;
overflow: hidden; overflow: hidden;
.search-pre { .search-pre {
width: 32px; width: 100%;
height: 32px; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.common-loading { .common-loading {
width: 16px; width: 14px;
height: 16px; height: 14px;
margin: 0; margin: 0;
} }
} }
@ -262,6 +262,13 @@
} }
} }
} }
&.nothing {
justify-content: center;
padding: 24px;
margin: 0;
border: 0;
line-height: 22px;
}
} }
} }
&.contacts { &.contacts {
@ -277,18 +284,31 @@
line-height: 34px; line-height: 34px;
border-bottom: 1px solid #efefef; border-bottom: 1px solid #efefef;
} }
&.loading, &.nothing,
&.loading {
text-align: center;
height: 100%;
margin: 0;
padding: 24px;
border-radius: 0;
line-height: 22px;
}
&.loading {
display: flex;
align-items: flex-start;
justify-content: center;
.common-loading {
width: 20px;
height: 20px;
margin: 1px;
}
}
&.loaded { &.loaded {
margin: 0; margin: 0;
height: 52px; height: 52px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.common-loading {
width: 20px;
height: 20px;
margin: 0;
}
} }
> ul { > ul {
> li { > li {