perf: 添加全局搜索功能

This commit is contained in:
kuaifan 2025-03-03 00:30:46 +08:00
parent 5e46b2cd1a
commit 1c4c4fe3fb
6 changed files with 157 additions and 45 deletions

View File

@ -36,11 +36,12 @@
<li class="item-label">{{typeLabel(type)}}</li> <li class="item-label">{{typeLabel(type)}}</li>
<li v-for="item in items" :key="item.id" @click="onClick(item)"> <li v-for="item in items" :key="item.id" @click="onClick(item)">
<div class="item-icon"> <div class="item-icon">
<EAvatar v-if="item.icons[0]==='avatar'" class="img-avatar" :src="item.icons[1]" :size="38"/> <div v-if="item.icons[0]==='file'" :class="`no-dark-content file-icon ${item.icons[1]}`"></div>
<i v-else-if="item.icons[0]==='department'" class="taskfont icon-avatar department">&#xe75c;</i> <i v-else-if="item.icons[0]==='department'" class="taskfont icon-avatar department">&#xe75c;</i>
<i v-else-if="item.icons[0]==='project'" class="taskfont icon-avatar project">&#xe6f9;</i> <i v-else-if="item.icons[0]==='project'" class="taskfont icon-avatar project">&#xe6f9;</i>
<i v-else-if="item.icons[0]==='task'" class="taskfont icon-avatar task">&#xe6f4;</i> <i v-else-if="item.icons[0]==='task'" class="taskfont icon-avatar task">&#xe6f4;</i>
<UserAvatar v-else-if="item.icons[0]==='user'" class="user-avatar" :userid="item.icons[1]" :size="38"/> <UserAvatar v-else-if="item.icons[0]==='user'" class="user-avatar" :userid="item.icons[1]" :size="38"/>
<EAvatar v-else-if="item.icons[0]==='avatar'" class="img-avatar" :src="item.icons[1]" :size="38"/>
<Icon v-else-if="item.icons[0]==='people'" class="icon-avatar" type="ios-people" /> <Icon v-else-if="item.icons[0]==='people'" class="icon-avatar" type="ios-people" />
<Icon v-else class="icon-avatar" type="md-person" /> <Icon v-else class="icon-avatar" type="md-person" />
</div> </div>
@ -54,7 +55,7 @@
</div> </div>
<div class="item-desc"> <div class="item-desc">
<span <span
class="desc-tag no-dark-content" class="desc-tag"
v-if="item.tags" v-if="item.tags"
v-for="tag in item.tags" v-for="tag in item.tags"
:style="tag.style">{{tag.name}}</span> :style="tag.style">{{tag.name}}</span>
@ -84,6 +85,7 @@ export default {
loadIng: 0, loadIng: 0,
searchKey: '', searchKey: '',
searchResults: [], searchResults: [],
searchTimer: null,
showModal: false, showModal: false,
} }
@ -129,14 +131,13 @@ export default {
list({searchKey, searchResults}) { list({searchKey, searchResults}) {
const items = searchResults.filter(item => item.key === searchKey) const items = searchResults.filter(item => item.key === searchKey)
const groups = {} const groups = {}
items.some(item => { items.forEach(item => {
if (!groups[item.type]) { if (!groups[item.type]) {
groups[item.type] = [] groups[item.type] = []
} }
if (groups[item.type].length > 10) { if (groups[item.type].length < 10) {
return true groups[item.type].push(item)
} }
groups[item.type].push(item)
}) })
return groups return groups
}, },
@ -197,9 +198,21 @@ export default {
onSearch() { onSearch() {
this.searchTask(this.searchKey) this.searchTask(this.searchKey)
this.searchProject(this.searchKey)
this.searchMessage(this.searchKey) this.searchMessage(this.searchKey)
this.searchContact(this.searchKey) this.searchContact(this.searchKey)
// todo searchFilesearchProject this.searchFile(this.searchKey)
},
pushResults(items) {
items.forEach(item => {
const index = this.searchResults.findIndex(({id, type}) => id === item.id && type === item.type)
if (index > -1) {
this.searchResults.splice(index, 1, item)
} else {
this.searchResults.push(item)
}
})
}, },
searchTask(key) { searchTask(key) {
@ -218,17 +231,17 @@ export default {
if (item.complete_at) { if (item.complete_at) {
tags.push({ tags.push({
name: this.$L('已完成'), name: this.$L('已完成'),
style: 'background-color:#666;color:#ddd', style: 'background-color:#ccc',
}) })
} else if (item.overdue) { } else if (item.overdue) {
tags.push({ tags.push({
name: this.$L('超期'), name: this.$L('超期'),
style: 'background-color:#f00;color:#ddd', style: 'background-color:#f00',
}) })
} else if ($A.dayjs(item.end_at).unix() - nowTime < 86400) { } else if ($A.dayjs(item.end_at).unix() - nowTime < 86400) {
tags.push({ tags.push({
name: this.$L('即将到期'), name: this.$L('即将到期'),
style: 'background-color:#f80;color:#ddd', style: 'background-color:#f80',
}) })
} }
return { return {
@ -245,14 +258,53 @@ export default {
rawData: item, rawData: item,
}; };
}); });
items.forEach(item => { this.pushResults(items)
const index = this.searchResults.findIndex(it => it.id === item.id && it.type === 'task') }).finally(_ => {
if (index > -1) { this.loadIng--;
this.searchResults.splice(index, 1, item) })
} else { },
this.searchResults.push(item)
searchProject(key) {
this.loadIng++;
this.$store.dispatch("call", {
url: 'project/lists',
data: {
keys: {
name: key
},
archived: 'all',
pagesize: 10,
},
}).then(({data}) => {
const items = data.data.map(item => {
const tags = [];
if (item.owner) {
tags.push({
name: this.$L('负责人'),
style: 'background-color:#0bc037',
})
} }
if (item.archived_at) {
tags.push({
name: this.$L('已归档'),
style: 'background-color:#ccc',
})
}
return {
key,
type: 'project',
icons: ['project', null],
tags,
id: item.id,
title: item.name,
desc: item.desc || '',
activity: item.updated_at,
rawData: item,
};
}) })
this.pushResults(items)
}).finally(_ => { }).finally(_ => {
this.loadIng--; this.loadIng--;
}) })
@ -301,14 +353,7 @@ export default {
rawData: item, rawData: item,
}; };
}) })
items.forEach(item => { this.pushResults(items)
const index = this.searchResults.findIndex(it => it.id === item.id && it.type === 'task')
if (index > -1) {
this.searchResults.splice(index, 1, item)
} else {
this.searchResults.push(item)
}
})
}).finally(_ => { }).finally(_ => {
this.loadIng--; this.loadIng--;
}) })
@ -332,24 +377,51 @@ export default {
id: item.userid, id: item.userid,
title: item.nickname, title: item.nickname,
desc: item?.profession || '', desc: item.profession || '',
activity: item.line_at, activity: item.line_at,
rawData: item, rawData: item,
}; };
}) })
items.forEach(item => { this.pushResults(items)
const index = this.searchResults.findIndex(it => it.id === item.id && it.type === 'contact')
if (index > -1) {
this.searchResults.splice(index, 1, item)
} else {
this.searchResults.push(item)
}
})
}).finally(_ => { }).finally(_ => {
this.loadIng--; this.loadIng--;
}) })
}, },
searchFile(key) {
this.loadIng++;
this.$store.dispatch("call", {
url: 'file/search',
data: {key},
}).then(({data}) => {
const items = data.map(item => {
const tags = [];
if (item.share) {
tags.push({
name: this.$L(item.userid == this.userId ? '已共享' : '共享'),
style: 'background-color:#0bc037',
})
}
return {
key,
type: 'file',
icons: ['file', item.type],
tags,
id: item.id,
title: item.name,
desc: item.type === 'folder' ? '' : $A.bytesToSize(item.size),
activity: item.updated_at,
rawData: item,
};
})
this.pushResults(items)
}).finally(_ => {
this.loadIng--;
})
}
} }
}; };
</script> </script>

View File

@ -976,6 +976,11 @@ export default {
this.onAddShow() this.onAddShow()
break; break;
case 70: // F -
e.preventDefault();
this.$refs.searchBox.onShow();
break;
case 75: case 75:
case 78: // K/N - case 78: // K/N -
e.preventDefault(); e.preventDefault();

View File

@ -7,8 +7,9 @@
<div class="dashboard-wrapper" :style="wrapperStyle"> <div class="dashboard-wrapper" :style="wrapperStyle">
<div class="dashboard-hello"> <div class="dashboard-hello">
<h2>{{dashboardHello}}</h2> <h2>{{dashboardHello}}</h2>
<div class="dashboard-search" @click="openSearch"> <div class="dashboard-search" :class="{'min-search': windowPortrait}" @click="openSearch">
<i class="taskfont">&#xe6f8;</i> <Icon type="ios-search"/>
<span>{{$L('搜索')}} ({{mateName}}+F)</span>
</div> </div>
</div> </div>
<div v-if="systemConfig.timezoneDifference" class="dashboard-time"> <div v-if="systemConfig.timezoneDifference" class="dashboard-time">
@ -121,6 +122,8 @@ export default {
loadIng: 0, loadIng: 0,
dashboard: 'today', dashboard: 'today',
mateName: /macintosh|mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl',
warningMsg: '', warningMsg: '',
hiddenColumns: hiddenCaches, hiddenColumns: hiddenCaches,

View File

@ -159,6 +159,7 @@
background-color: #ffffff !important; background-color: #ffffff !important;
} }
.file-icon,
.img-avatar, .img-avatar,
.user-avatar, .user-avatar,
.icon-avatar { .icon-avatar {
@ -169,6 +170,14 @@
flex-shrink: 0; flex-shrink: 0;
} }
.file-icon {
display: flex;
&:before {
width: 100%;
height: 100%;
}
}
.img-avatar { .img-avatar {
display: flex; display: flex;
align-items: center; align-items: center;
@ -243,8 +252,8 @@
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
margin-right: 6px; margin-right: 6px;
background-color: #f0f0f0; background-color: #cccccc;
color: #303133; color: #ffffff;
word-break: keep-all; word-break: keep-all;
} }

View File

@ -641,6 +641,13 @@ body.dark-mode-reverse {
.icon-avatar { .icon-avatar {
color: #1c1917; color: #1c1917;
} }
.item-content {
.item-desc {
.desc-tag {
color: #1c1917;
}
}
}
} }
} }
} }

View File

@ -44,7 +44,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
> h2 { > h2 {
flex: 3; flex: 1;
color: $primary-title-color; color: $primary-title-color;
font-size: 24px; font-size: 24px;
font-weight: 600; font-weight: 600;
@ -54,28 +54,44 @@
word-wrap: break-word; word-wrap: break-word;
} }
.dashboard-search { .dashboard-search {
flex: 1; flex-shrink: 0;
display: flex; display: flex;
max-width: 180px; min-width: 120px;
max-width: 220px;
margin-left: 24px;
height: 34px; height: 34px;
align-items: center; align-items: center;
justify-content: center; justify-content: flex-start;
padding: 0 2px 0 24px; padding: 0 12px;
border-radius: 8px; border-radius: 8px;
color: #515a6e; color: #515a6e;
background-color: #fff; background-color: #F4F5F7;
background-image: none; background-image: none;
cursor: pointer; cursor: pointer;
opacity: 0.8; opacity: 0.8;
transition: opacity 0.3s; transition: opacity 0.3s;
flex: none;
> i { > i {
font-size: 20px; font-size: 22px;
cursor: pointer;
}
> span {
padding: 0 4px 0 8px;
cursor: pointer; cursor: pointer;
} }
&:hover { &:hover {
opacity: 1; opacity: 1;
} }
&.min-search {
min-width: auto;
background-color: transparent;
opacity: 1;
> i {
font-size: 24px;
}
> span {
display: none;
}
}
} }
} }
.dashboard-time, .dashboard-time,