style(admin): unify dialog layouts and dense readonly presentation
This commit is contained in:
@@ -27,6 +27,9 @@ const detailOpen = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const detailError = ref<ApiErrorDetails | null>(null)
|
||||
const detail = ref<NotificationInboxDetail | null>(null)
|
||||
const detailRow = ref<NotificationInboxItem | null>(null)
|
||||
let detailRequest = 0
|
||||
watch(detailOpen, value => { if (!value) { detailRequest++; detailRow.value = null } })
|
||||
const access = useAccessPreviewStore()
|
||||
const canRead = computed(() => access.can('platform:notification:read'))
|
||||
const canReadAll = computed(() => access.can('platform:notification:read-all'))
|
||||
@@ -58,14 +61,20 @@ async function load() {
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
async function openDetail(row: NotificationInboxItem) {
|
||||
const request = ++detailRequest
|
||||
detailRow.value = row
|
||||
detailOpen.value = true; detailLoading.value = true; detailError.value = null; detail.value = null
|
||||
try {
|
||||
detail.value = await getMyNotification(row.id)
|
||||
if (detail.value.status === 'UNREAD' && canRead.value) {
|
||||
detail.value = await markMyNotificationRead(row.id)
|
||||
const result = await getMyNotification(row.id)
|
||||
if (request !== detailRequest || !detailOpen.value) return
|
||||
detail.value = result
|
||||
if (result.status === 'UNREAD' && canRead.value) {
|
||||
const read = await markMyNotificationRead(row.id)
|
||||
if (request !== detailRequest || !detailOpen.value) return
|
||||
detail.value = read
|
||||
await load()
|
||||
}
|
||||
} catch (reason) { detailError.value = getApiErrorDetails(reason, '消息详情加载失败') } finally { detailLoading.value = false }
|
||||
} catch (reason) { if (request === detailRequest && detailOpen.value) detailError.value = getApiErrorDetails(reason, '消息详情加载失败') } finally { if (request === detailRequest) detailLoading.value = false }
|
||||
}
|
||||
async function markRead(row: NotificationInboxItem) {
|
||||
if (!canRead.value || row.status !== 'UNREAD') return
|
||||
@@ -97,16 +106,21 @@ onMounted(() => void load())
|
||||
<template #status="{ row }"><el-tag :type="(row as NotificationInboxItem).status === 'UNREAD' ? 'danger' : 'info'">{{ statusLabel((row as NotificationInboxItem).status) }}</el-tag></template>
|
||||
</DataTable></section>
|
||||
</ListPageLayout>
|
||||
<AppDrawer class="admin-density admin-density-drawer" v-model="detailOpen" title="消息详情" size="min(680px, 94vw)" :loading="detailLoading" :show-footer="false" destroy-on-close>
|
||||
<LoadErrorState v-if="detailError" title="消息详情加载失败" :message="detailError.message" :loading="detailLoading"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
|
||||
<article v-else-if="detail" class="notification-detail"><header><h2>{{ detail.title }}</h2><el-tag :type="detail.status === 'UNREAD' ? 'danger' : 'info'">{{ statusLabel(detail.status) }}</el-tag></header><p class="meta">{{ notificationCategoryLabel(detail.category) }} · {{ formatDate(detail.sentAt) }}</p><div class="body">{{ detail.body || '暂无消息内容' }}</div></article>
|
||||
<AppDrawer class="admin-density admin-density-drawer admin-density-drawer--detail" v-model="detailOpen" title="消息详情" size="min(680px, 94vw)" :loading="detailLoading" :show-footer="false" destroy-on-close>
|
||||
<LoadErrorState v-if="detailError" title="消息详情加载失败" :message="detailError.message" :loading="detailLoading" @retry="detailRow && openDetail(detailRow)"><template #details><ApiErrorMeta :details="detailError" /></template></LoadErrorState>
|
||||
<article v-else-if="detail" class="density-dialog-sections notification-detail">
|
||||
<section class="density-dialog-section" data-tone="summary" aria-label="消息资料">
|
||||
<h3 class="density-dialog-section__title message-heading"><span>{{ detail.title || '消息详情' }}</span><el-tag :type="detail.status === 'UNREAD' ? 'danger' : 'info'">{{ statusLabel(detail.status) }}</el-tag></h3>
|
||||
<p class="density-dialog-note">{{ notificationCategoryLabel(detail.category) }} · {{ formatDate(detail.sentAt) }}</p>
|
||||
</section>
|
||||
<section class="density-dialog-section" data-tone="source" aria-label="消息正文"><h3 class="density-dialog-section__title">消息正文</h3><div class="message-body">{{ detail.body || '暂无消息内容' }}</div></section>
|
||||
</article>
|
||||
</AppDrawer>
|
||||
</AdminPage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.notification-detail header { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-5); }
|
||||
.notification-detail h2 { min-width: 0; margin: 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: var(--admin-font-title); line-height: 1.4; }
|
||||
.notification-detail .meta { margin: var(--admin-space-5) 0; color: var(--cc-muted); font-size: var(--admin-font-caption); }
|
||||
.notification-detail .body { padding-top: var(--admin-space-6); border-top: 1px solid var(--cc-border); color: var(--cc-text); white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.8; }
|
||||
.message-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--admin-space-5); }
|
||||
.message-heading > span { min-width: 0; overflow-wrap: anywhere; }
|
||||
.message-body { white-space: pre-wrap; overflow-wrap: anywhere; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user