Files
EmailBill/Web/src/views/MessageView.vue

298 lines
6.9 KiB
Vue
Raw Normal View History

<!-- eslint-disable vue/no-v-html -->
<template>
2025-12-30 17:02:30 +08:00
<div class="page-container-flex">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell-group v-if="list.length" inset style="margin-top: 10px">
<van-swipe-cell v-for="item in list" :key="item.id">
<div class="message-card" @click="viewDetail(item)">
<div class="card-left">
<div class="message-title" :class="{ 'unread': !item.isRead }">
{{ item.title }}
</div>
2025-12-29 16:07:43 +08:00
<div class="message-content">
{{ item.content }}
</div>
<div class="message-info">
{{ item.createTime }}
</div>
</div>
<div class="card-right">
<van-tag v-if="!item.isRead" type="danger">未读</van-tag>
<van-icon name="arrow" size="16" class="arrow-icon" />
</div>
</div>
<template #right>
<van-button square text="删除" type="danger" class="delete-button" @click="handleDelete(item)" />
</template>
</van-swipe-cell>
</van-cell-group>
<van-empty v-else-if="!loading" description="暂无消息" />
</van-list>
</van-pull-refresh>
<!-- 详情弹出层 -->
2025-12-30 17:02:30 +08:00
<PopupContainer
v-model="detailVisible"
:title="currentMessage.title"
:subtitle="currentMessage.createTime"
height="80%"
2025-12-30 17:02:30 +08:00
:closeable="true"
>
<div
v-if="currentMessage.messageType === 2"
class="detail-content rich-html-content"
v-html="currentMessage.content"
>
</div>
<div v-else class="detail-content">
2025-12-30 17:02:30 +08:00
{{ currentMessage.content }}
</div>
2025-12-30 17:02:30 +08:00
</PopupContainer>
2025-12-28 10:23:57 +08:00
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { showToast, showDialog } from 'vant';
import { getMessageList, markAsRead, deleteMessage, markAllAsRead } from '@/api/message';
import { useMessageStore } from '@/stores/message';
2025-12-30 17:02:30 +08:00
import PopupContainer from '@/components/PopupContainer.vue';
const messageStore = useMessageStore();
const list = ref([]);
const loading = ref(false);
const finished = ref(false);
const refreshing = ref(false);
const pageIndex = ref(1);
const pageSize = ref(20);
const detailVisible = ref(false);
const currentMessage = ref({});
const onLoad = async () => {
if (refreshing.value) {
list.value = [];
pageIndex.value = 1;
refreshing.value = false;
}
try {
const res = await getMessageList({
pageIndex: pageIndex.value,
pageSize: pageSize.value
});
if (res.success) {
// 格式化时间
const data = res.data.map(item => ({
...item,
createTime: new Date(item.createTime).toLocaleString()
}));
if (pageIndex.value === 1) {
list.value = data;
} else {
list.value = [...list.value, ...data];
}
// 判断是否加载完成
if (list.value.length >= res.total || data.length < pageSize.value) {
finished.value = true;
} else {
pageIndex.value++;
}
} else {
showToast(res.message || '加载失败');
finished.value = true;
}
} catch (error) {
console.error(error);
showToast('加载失败');
finished.value = true;
} finally {
loading.value = false;
}
};
const onRefresh = () => {
finished.value = false;
loading.value = true;
onLoad();
};
const viewDetail = async (item) => {
if (!item.isRead) {
try {
await markAsRead(item.id);
item.isRead = true;
messageStore.updateUnreadCount();
} catch (error) {
console.error('标记已读失败', error);
}
}
if (item.messageType === 1) {
if (item.content.startsWith('http')) {
window.open(item.content, '_blank');
} else {
showToast('无效的URL');
}
return;
}
currentMessage.value = item;
detailVisible.value = true;
};
const handleDelete = (item) => {
showDialog({
title: '提示',
message: '确定要删除这条消息吗?',
showCancelButton: true,
}).then(async (action) => {
if (action === 'confirm') {
try {
const res = await deleteMessage(item.id);
if (res.success) {
showToast('删除成功');
const wasUnread = !item.isRead;
list.value = list.value.filter(i => i.id !== item.id);
if (wasUnread) {
messageStore.updateUnreadCount();
}
} else {
showToast(res.message || '删除失败');
}
} catch (error) {
2025-12-29 16:07:43 +08:00
console.error('删除消息失败', error);
showToast('删除失败');
}
}
});
};
const handleMarkAllRead = () => {
showDialog({
title: '提示',
message: '确定要将所有消息标记为已读吗?',
showCancelButton: true,
}).then(async (action) => {
if (action === 'confirm') {
try {
const res = await markAllAsRead();
if (res.success) {
showToast('操作成功');
// 刷新列表
onRefresh();
// 更新未读计数
messageStore.updateUnreadCount();
} else {
showToast(res.message || '操作失败');
}
} catch (error) {
2025-12-29 16:07:43 +08:00
console.error('标记所有已读失败', error);
showToast('操作失败');
}
}
});
};
onMounted(() => {
// onLoad 会由 van-list 自动触发
});
defineExpose({
handleMarkAllRead
});
</script>
<style scoped>
.message-card {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
cursor: pointer;
}
.card-left {
flex: 1;
min-width: 0;
padding-right: 12px;
}
.card-right {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.arrow-icon {
color: var(--van-gray-5);
}
.message-title {
font-size: 15px;
margin-bottom: 6px;
color: var(--van-text-color);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
2025-12-29 16:07:43 +08:00
.message-content{
font-size: 14px;
color: var(--van-text-color-2);
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.message-title.unread {
font-weight: bold;
}
.message-info {
font-size: 12px;
color: var(--van-text-color-2);
}
.delete-button {
height: 100%;
}
.detail-time {
color: var(--van-text-color-2);
font-size: 14px;
}
.detail-content {
padding: 16px;
font-size: 14px;
line-height: 1.6;
color: var(--van-text-color);
}
.detail-content:not(.rich-html-content) {
white-space: pre-wrap;
}
2025-12-30 17:02:30 +08:00
:deep(.van-pull-refresh) {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
/* 设置页面容器背景色 */
:deep(.van-nav-bar) {
background: transparent !important;
}
</style>