Files
Aida_Purchaser_Front/src/views/notifications/index.vue

249 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="notifications-view mini-scrollbar" @scroll="handleScroll">
<section class="notifications-hero">
<div class="notifications-hero__content">
<h1 class="notifications-hero__title">Notifications</h1>
<p class="notifications-hero__subtitle">System announcements and updates</p>
</div>
</section>
<section class="notifications-content">
<NotificationsList
:items="notifications"
:unread-count="unreadCount"
@toggle-item="handleToggleItem"
@mark-all-as-read="handleMarkAllAsRead"
/>
<div v-if="loading" class="loading-indicator">Loading...</div>
<div v-if="!hasMore && notifications.length > 0" class="no-more-data">No more notifications</div>
</section>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import NotificationsList from './components/NotificationsList.vue'
import type { NotificationRecord } from './types'
import { fetchAllMessageList, markMessageAsRead, markAllMessagesAsRead } from '@/api/notification'
const { locale } = useI18n()
// Store notifications with original date strings
interface NotificationWithOriginalDate extends NotificationRecord {
originalDate: string
}
const notificationsRaw = ref<NotificationWithOriginalDate[]>([])
const loading = ref(false)
const hasMore = ref(true)
/**
* Format date based on current language
* Chinese: yyyy年M月D日 (e.g., 2024年5月15日)
* English: Month Day, Year (e.g., January 15, 2024)
*/
const formatDate = (dateString: string): string => {
if (!dateString) return ''
const date = new Date(dateString)
if (isNaN(date.getTime())) return dateString
const isChinese = locale.value === 'CHINESE_SIMPLIFIED'
if (isChinese) {
// Chinese format: yyyy年M月D日 (without leading zeros)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}${month}${day}`
} else {
// English format: Month Day, Year
const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'long',
day: 'numeric'
}
return date.toLocaleDateString('en-US', options)
}
}
// Computed property that formats dates based on current locale
const notifications = computed<NotificationRecord[]>(() => {
return notificationsRaw.value.map((item) => ({
id: item.id,
title: item.title,
date: formatDate(item.originalDate),
content: item.content,
isUnread: item.isUnread,
isExpanded: item.isExpanded
}))
})
const unreadCount = computed(() => notifications.value.filter((item) => item.isUnread).length)
const handleToggleItem = async (id: NotificationRecord['id']) => {
const targetItem = notificationsRaw.value.find((item) => item.id === id)
if (!targetItem) return
const nextExpanded = !targetItem.isExpanded
// 如果消息是未读状态调用API标记为已读
if (targetItem.isUnread) {
try {
await markMessageAsRead(id)
} catch (error) {
console.error('Failed to mark message as read:', error)
}
}
notificationsRaw.value = notificationsRaw.value.map((item) => ({
...item,
isUnread: item.id === id ? false : item.isUnread,
isExpanded: item.id === id ? nextExpanded : false
}))
}
const handleMarkAllAsRead = async () => {
try {
await markAllMessagesAsRead()
notificationsRaw.value = notificationsRaw.value.map((item) => ({
...item,
isUnread: false
}))
} catch (error) {
console.error('Failed to mark all messages as read:', error)
}
}
const params = ref({
page: 1,
size: 15
})
const handleFetchMessageList = async () => {
if (loading.value || !hasMore.value) return
loading.value = true
try {
const res = await fetchAllMessageList(params.value)
// Transform API data to match NotificationRecord interface with original date
const newNotifications: NotificationWithOriginalDate[] = res.content.map((item: any) => ({
id: String(item.id),
title: item.title,
date: item.createTime, // This will be formatted by computed property
originalDate: item.createTime, // Store original date for re-formatting
content: item.content,
isUnread: item.isRead === 0,
isExpanded: false
}))
if (params.value.page === 1) {
notificationsRaw.value = newNotifications
} else {
notificationsRaw.value = [...notificationsRaw.value, ...newNotifications]
}
// Check if there are more pages
hasMore.value = params.value.page < res.pages
} catch (error) {
console.error('Failed to fetch notifications:', error)
} finally {
loading.value = false
}
}
const handleScroll = (event: Event) => {
const target = event.target as HTMLElement
const scrollTop = target.scrollTop
const scrollHeight = target.scrollHeight
const clientHeight = target.clientHeight
// Load more when scrolled to 80% of the content
if (scrollTop + clientHeight >= scrollHeight * 0.8 && !loading.value && hasMore.value) {
params.value.page++
handleFetchMessageList()
}
}
onMounted(() => {
handleFetchMessageList()
})
</script>
<style lang="less" scoped>
.notifications-view {
height: 100%;
overflow-y: auto;
background: #ffffff;
}
.notifications-hero {
min-height: 14.8rem;
padding: 3.2rem 2rem;
display: flex;
align-items: center;
justify-content: center;
background: url('@/assets/images/wardrobe/settings_bg.jpg') no-repeat;
background-size: cover;
border-bottom: 0.05rem solid #dfd8d1;
}
.notifications-hero__content {
text-align: center;
}
.notifications-hero__title {
margin: 0;
color: #232323;
font-size: 4rem;
line-height: 1;
font-family: 'KaiseiOpti-Bold';
}
.notifications-hero__subtitle {
margin: 1.2rem 0 0;
color: #585858;
font-size: 1.6rem;
line-height: 1.4;
font-family: 'KaiseiOpti-Regular';
}
.notifications-content {
width: min(108rem, calc(100% - 4rem));
margin: 0 auto;
padding: 4rem 0 8rem;
}
.loading-indicator,
.no-more-data {
text-align: center;
padding: 2rem;
color: #979797;
font-size: 1.4rem;
font-family: 'KaiseiOpti-Regular';
}
@media (max-width: 768px) {
.notifications-hero {
min-height: 12rem;
padding: 2.8rem 1.6rem;
}
.notifications-hero__title {
font-size: 3rem;
}
.notifications-hero__subtitle {
margin-top: 0.8rem;
font-size: 1.4rem;
}
.notifications-content {
width: calc(100% - 3.2rem);
padding: 3.2rem 0 4.8rem;
}
}
</style>