如何遍历嵌套Folder结构并实现Outlook风格邮件搜索过滤
复刻Outlook嵌套文件夹邮件搜索功能
需求说明
基于给定的多层嵌套Folder/Email TypeScript接口结构(层级深度未知),结合Vuetify的v-text-field搜索输入框,实现以下功能:
- 实时根据搜索词筛选邮件,严格维持原有文件夹层级结构
- 仅保留包含至少一封匹配邮件的文件夹(包括父文件夹,即使父文件夹自身无匹配邮件,但子层级有)
- 每个保留的文件夹中仅保留匹配搜索词的邮件
核心实现逻辑
- 邮件匹配规则:遍历邮件的所有可搜索属性,进行不区分大小写的模糊匹配,包括:
- 发件人/收件人/抄送人/密送人的姓名、邮箱地址
- 邮件主题、正文、正文预览
- 附件的文件名
- 递归文件夹处理:因为文件夹层级未知,必须用递归遍历所有层级:
- 先筛选当前文件夹下的匹配邮件
- 再递归处理子文件夹,得到筛选后的子文件夹列表
- 若当前文件夹存在匹配邮件,或存在筛选后的子文件夹,则保留该文件夹,并替换其邮件列表和子文件夹列表为筛选结果
具体代码实现
1. TypeScript工具函数
// 检查单封邮件是否匹配搜索词 const isEmailMatch = (email: Email, searchTerm: string): boolean => { if (!searchTerm.trim()) return true; // 空搜索词返回全部 const lowerSearch = searchTerm.toLowerCase(); // 检查联系人列表(to/cc/bcc) const checkContacts = (contacts: Contact[]): boolean => { return contacts.some(contact => (contact.name?.toLowerCase().includes(lowerSearch)) || (contact.email_address.toString().includes(lowerSearch)) ); }; // 检查附件文件名 const checkAttachments = (attachments: FileAttachment[]): boolean => { return attachments.some(att => att.file_name.toLowerCase().includes(lowerSearch)); }; // 综合所有可搜索属性 return ( email.from.name?.toLowerCase().includes(lowerSearch) || email.from.email_address.toString().includes(lowerSearch) || checkContacts(email.to) || checkContacts(email.cc) || checkContacts(email.bcc) || email.subject.toLowerCase().includes(lowerSearch) || email.body.toLowerCase().includes(lowerSearch) || email.body_preview.toLowerCase().includes(lowerSearch) || checkAttachments(email.file_attachments) ); }; // 递归筛选文件夹结构 const filterFolders = (folders: Folder[], searchTerm: string): Folder[] => { return folders.reduce((acc, folder) => { // 筛选当前文件夹下的匹配邮件 const filteredEmails = folder.emails.filter(email => isEmailMatch(email, searchTerm)); // 递归处理子文件夹 const filteredSubFolders = folder.folders ? filterFolders(folder.folders, searchTerm) : []; // 只有当存在匹配邮件或非空的子文件夹时,才保留当前文件夹 if (filteredEmails.length > 0 || filteredSubFolders.length > 0) { acc.push({ ...folder, emails: filteredEmails, folders: filteredSubFolders.length > 0 ? filteredSubFolders : null }); } return acc; }, [] as Folder[]); };
2. Vue组件中集成
假设你的组件中有原始文件夹数据originalFolders,结合Vuetify的搜索框,使用计算属性实时返回筛选后的结果:
<template> <v-text-field v-model="search" append-inner-icon="mdi-magnify" label="动态搜索" density="compact" variant="outlined" clearable rounded hide-details single-line flat color="primaryDarken4" /> <!-- 这里是你的文件夹/邮件展示组件,绑定filteredFolders --> <mail-folder-tree :folders="filteredFolders" /> </template> <script setup lang="ts"> import { ref, computed } from 'vue'; import type { Folder } from '@/types'; const search = ref(''); const originalFolders = ref<Folder[]>([]); // 假设这里是从后端获取的原始文件夹数据 const filteredFolders = computed(() => { if (!search.value.trim()) return originalFolders.value; return filterFolders(originalFolders.value, search.value); }); // 引入上面定义的isEmailMatch和filterFolders函数 </script>
注意事项
- 若邮件数量极大,建议在后端做搜索优化(比如全文索引),前端仅做轻量筛选
- 可以根据需求调整匹配规则,比如添加日期范围筛选、附件类型筛选等
- 递归处理时注意性能,若层级极深可考虑尾递归优化
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

