mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-09-05 15:38:34 +08:00
feat: 增加全宽列表视图功能 (#1079)
* feat(mailbox): add list view mode - Add a toggleable list view for the mailbox, with a settings option and back button. - deselect mail on second click in list view - set current mail on row click in multi-action mode * feat(mailbox): add configurable body preview line clamp Allow users to set the number of preview lines (0–5) for mail body in the list view via a slider in Appearance settings. Includes i18n support for the new option and its "Off" state. * chore: clarify some i18n message in settings include the following changes: - The original "Mailbox Split Size" to "Left list width in two-column mailbox view" - The description of new feature "Full-width mailbox list view" sync all languages with the updated message * docs: update changelog with recent UI improvements - Added mailbox full-width list view and body preview lines settings - Extended left panel width ratio range to 0 - Included English changelog translations * docs: fix CHANGELOG improvements types * fix: enable mail list preview line clamp settings on mobile
This commit is contained in:
@@ -60,7 +60,7 @@ const props = defineProps({
|
||||
const localFilterKeyword = ref('')
|
||||
|
||||
const {
|
||||
isDark, mailboxSplitSize, indexTab, loading, useUTCDate,
|
||||
isDark, mailboxSplitSize, mailListView, mailListPreviewLineClamp, indexTab, loading, useUTCDate,
|
||||
autoRefresh, configAutoRefreshInterval, sendMailModel
|
||||
} = useGlobalState()
|
||||
const autoRefreshInterval = ref(configAutoRefreshInterval.value)
|
||||
@@ -71,6 +71,12 @@ const count = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
|
||||
const mailListPreviewLineClampValue = computed(() => {
|
||||
const value = Number(mailListPreviewLineClamp.value)
|
||||
if (!Number.isFinite(value)) return 0
|
||||
return Math.min(5, Math.max(0, Math.round(value)))
|
||||
})
|
||||
|
||||
// Computed property for filtered data (only filter current page)
|
||||
const data = computed(() => {
|
||||
if (!localFilterKeyword.value || localFilterKeyword.value.trim() === '') {
|
||||
@@ -183,7 +189,7 @@ const refresh = async () => {
|
||||
count.value = totalCount;
|
||||
}
|
||||
curMail.value = null;
|
||||
if (!isMobile.value && data.value.length > 0) {
|
||||
if (!isMobile.value && !mailListView.value && data.value.length > 0) {
|
||||
curMail.value = data.value[0];
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -202,6 +208,11 @@ const backFirstPageAndRefresh = async () => {
|
||||
const clickRow = async (row) => {
|
||||
if (multiActionMode.value) {
|
||||
row.checked = !row.checked;
|
||||
curMail.value = row;
|
||||
return;
|
||||
}
|
||||
if (mailListView.value && curMail.value?.id === row.id) {
|
||||
curMail.value = null;
|
||||
return;
|
||||
}
|
||||
curMail.value = row;
|
||||
@@ -375,8 +386,13 @@ onBeforeUnmount(() => {
|
||||
clearable />
|
||||
</n-space>
|
||||
</div>
|
||||
<n-split class="left" direction="horizontal" :max="0.75" :min="0.25" :default-size="mailboxSplitSize"
|
||||
:on-update:size="onSpiltSizeChange">
|
||||
<n-split class="left" direction="horizontal" :max="0.75" :min="0" :resize-trigger-size="8"
|
||||
:default-size="mailboxSplitSize" :on-update:size="onSpiltSizeChange" v-if="!mailListView || curMail">
|
||||
<template #resize-trigger>
|
||||
<div class="split-handle">
|
||||
<div class="split-handle__grip" />
|
||||
</div>
|
||||
</template>
|
||||
<template #1>
|
||||
<div style="overflow: auto; min-height: 60vh; max-height: 100vh;">
|
||||
<n-list hoverable clickable>
|
||||
@@ -412,15 +428,25 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
<template #2>
|
||||
<div v-if="curMail" style="margin: 8px;">
|
||||
<n-flex justify="space-between">
|
||||
<n-button @click="prevMail" :disabled="!canGoPrevMail" text size="small">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ArrowBackIosNewFilled />
|
||||
</n-icon>
|
||||
</template>
|
||||
{{ t('prevMail') }}
|
||||
</n-button>
|
||||
<n-flex justify="space-between" align="center">
|
||||
<n-space :wrap="false" align="center">
|
||||
<n-button v-if="mailListView" @click="curMail = null" text size="small">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ArrowBackIosNewFilled />
|
||||
</n-icon>
|
||||
</template>
|
||||
{{ t('backToList') }}
|
||||
</n-button>
|
||||
<n-button @click="prevMail" :disabled="!canGoPrevMail" text size="small">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ArrowBackIosNewFilled />
|
||||
</n-icon>
|
||||
</template>
|
||||
{{ t('prevMail') }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-button @click="nextMail" :disabled="!canGoNextMail" text size="small" icon-placement="right">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
@@ -446,6 +472,48 @@ onBeforeUnmount(() => {
|
||||
</n-card>
|
||||
</template>
|
||||
</n-split>
|
||||
<div v-else class="mail-list-scroll">
|
||||
<n-list hoverable clickable>
|
||||
<n-list-item v-for="row in data" v-bind:key="row.id" @click="() => clickRow(row)"
|
||||
:class="mailItemClass(row)">
|
||||
<template #prefix v-if="multiActionMode">
|
||||
<n-checkbox v-model:checked="row.checked" />
|
||||
</template>
|
||||
<n-thing class="mail-list-thing">
|
||||
<template #header>
|
||||
<n-ellipsis class="mail-list-title">
|
||||
{{ row.subject }}
|
||||
</n-ellipsis>
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="mail-list-meta">
|
||||
<n-tag type="info">
|
||||
ID: {{ row.id }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
{{ utcToLocalDate(row.created_at, useUTCDate) }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
<n-ellipsis class="mail-list-meta-text">
|
||||
{{ showEMailTo ? "FROM: " + row.source : row.source }}
|
||||
</n-ellipsis>
|
||||
</n-tag>
|
||||
<n-tag v-if="showEMailTo" type="info">
|
||||
<n-ellipsis class="mail-list-meta-text">
|
||||
TO: {{ row.address }}
|
||||
</n-ellipsis>
|
||||
</n-tag>
|
||||
<AiExtractInfo :metadata="row.metadata" compact />
|
||||
</div>
|
||||
</template>
|
||||
<n-ellipsis v-if="row.text && mailListPreviewLineClampValue > 0"
|
||||
:line-clamp="mailListPreviewLineClampValue" class="mail-list-preview" :tooltip="false">
|
||||
{{ row.text }}
|
||||
</n-ellipsis>
|
||||
</n-thing>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</div>
|
||||
</div>
|
||||
<div class="left" v-else>
|
||||
<n-space justify="space-around" align="center" :wrap="false" style="display: flex; align-items: center;">
|
||||
@@ -555,8 +623,80 @@ onBeforeUnmount(() => {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mail-list-scroll {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
min-height: 60vh;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.mail-list-thing,
|
||||
.mail-list-title,
|
||||
.mail-list-preview {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mail-list-thing,
|
||||
.mail-list-preview {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mail-list-thing :deep(.n-thing-main),
|
||||
.mail-list-thing :deep(.n-thing-header),
|
||||
.mail-list-thing :deep(.n-thing-header__title),
|
||||
.mail-list-thing :deep(.n-thing-main__description),
|
||||
.mail-list-thing :deep(.n-thing-main__content) {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mail-list-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mail-list-meta :deep(.n-tag) {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.mail-list-meta-text {
|
||||
max-width: min(240px, 100%);
|
||||
}
|
||||
|
||||
.mail-list-preview {
|
||||
display: -webkit-box;
|
||||
overflow-wrap: anywhere;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mail-list-scroll :deep(.n-list-item__main) {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.split-handle {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.split-handle__grip {
|
||||
width: 4px;
|
||||
height: 32px;
|
||||
border-radius: 2px;
|
||||
background-color: var(--n-resize-trigger-color);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.split-handle:hover .split-handle__grip {
|
||||
background-color: var(--n-resize-trigger-color-hover);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -210,8 +210,13 @@ onMounted(async () => {
|
||||
</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
<n-split direction="horizontal" :max="0.75" :min="0.25" :default-size="mailboxSplitSize"
|
||||
:on-update:size="onSpiltSizeChange">
|
||||
<n-split direction="horizontal" :max="0.75" :min="0" :resize-trigger-size="8"
|
||||
:default-size="mailboxSplitSize" :on-update:size="onSpiltSizeChange">
|
||||
<template #resize-trigger>
|
||||
<div class="split-handle">
|
||||
<div class="split-handle__grip" />
|
||||
</div>
|
||||
</template>
|
||||
<template #1>
|
||||
<div style="overflow: auto; min-height: 60vh; max-height: 100vh;">
|
||||
<n-list hoverable clickable>
|
||||
@@ -379,4 +384,23 @@ pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.split-handle {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.split-handle__grip {
|
||||
width: 4px;
|
||||
height: 32px;
|
||||
border-radius: 2px;
|
||||
background-color: var(--n-resize-trigger-color);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.split-handle:hover .split-handle__grip {
|
||||
background-color: var(--n-resize-trigger-color-hover);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user