diff --git a/src/utils/jmap.ts b/src/utils/jmap.ts index d8e8a1a..700c5b8 100644 --- a/src/utils/jmap.ts +++ b/src/utils/jmap.ts @@ -91,11 +91,13 @@ export class JmapClient { return payload.list || [] } - /** 查询指定邮箱内的邮件 id(默认收件箱,按接收时间倒序) */ - async queryEmailIds(mailboxId: string, limit = 50): Promise { + /** 查询邮箱内邮件 id(mailboxId 为空时查全部;按接收时间倒序) */ + async queryEmailIds(mailboxId: string | null = null, limit = 50): Promise { + const filter: Record = {} + if (mailboxId) filter.inMailbox = mailboxId const payload = await this.call<{ ids: string[] }>('Email/query', { accountId: this.accountId, - filter: { inMailbox: mailboxId }, + filter, sort: [{ property: 'receivedAt', isAscending: false }], limit, }) @@ -132,6 +134,32 @@ export class JmapClient { }) } + /** 批量标记已读/未读 */ + async setSeenBatch(emailIds: string[], seen: boolean): Promise { + if (!emailIds.length) return + const update: Record = {} + for (const id of emailIds) { + update[id] = { keywords: seen ? { $seen: true } : { $seen: false } } + } + await this.call('Email/set', { accountId: this.accountId, update }) + } + + /** 批量删除:传入 fromMailboxId 且有垃圾箱时移入垃圾箱;否则直接销毁 */ + async deleteEmailBatch(emailIds: string[], fromMailboxId?: string): Promise { + if (!emailIds.length) return + const mailboxes = await this.getMailboxes() + const trash = mailboxes.find((m) => m.role === 'trash') + if (!fromMailboxId || !trash) { + await this.call('Email/set', { accountId: this.accountId, destroy: emailIds }) + return + } + const update: Record = {} + for (const id of emailIds) { + update[id] = { mailboxIds: { [trash.id]: true, [fromMailboxId]: false } } + } + await this.call('Email/set', { accountId: this.accountId, update }) + } + /** 获取发信身份(Identity) */ private async getIdentityId(): Promise { const payload = await this.call<{ list: Array<{ id: string }> }>('Identity/get', { accountId: this.accountId, ids: null }) @@ -148,6 +176,8 @@ export class JmapClient { subject: string textBody?: string htmlBody?: string + inReplyTo?: string[] + references?: string[] }): Promise { const mailboxes = await this.getMailboxes() const drafts = mailboxes.find((m) => m.role === 'drafts') || mailboxes[0] @@ -160,6 +190,9 @@ export class JmapClient { to: opts.to.map((t) => ({ name: t.name || '', email: t.email })), subject: opts.subject, } + // 回复/转发时携带引用头,形成会话线程 + if (opts.inReplyTo?.length) emailData.inReplyTo = opts.inReplyTo + if (opts.references?.length) emailData.references = opts.references // 正文必须用标准 JMAP bodyValues + partId 引用方式: // Stalwart v0.11 不允许在 textBody/htmlBody 数组里内联 value(报 invalidProperties "Cannot set property.") if (opts.htmlBody) { diff --git a/src/views/mail/Mailbox.vue b/src/views/mail/Mailbox.vue index fbde942..da3c981 100644 --- a/src/views/mail/Mailbox.vue +++ b/src/views/mail/Mailbox.vue @@ -1,20 +1,25 @@ @@ -291,74 +481,119 @@ async function openMailbox(): Promise { - + - - - - - - - - + + @@ -422,7 +640,7 @@ async function openMailbox(): Promise { - +