topfans/frontend/pages/mailbox/components/PostcardBase.vue

110 lines
3.2 KiB
Vue

<template>
<view class="postcard-base">
<view class="postcard-header">
<text class="postcard-title">{{ displayTitle }}</text>
<view class="postcard-meta">
<text class="meta-type">[{{ typeLabelText }}]</text>
<text class="meta-time">{{ formatTime(notification.created_at) }}</text>
</view>
<!-- 收件人/ context:用户做主语,star context,语义清晰 -->
<view v-if="userNickname || userMobile || starName" class="postcard-user">
<text v-if="userNickname" class="user-nick">{{ userNickname }}</text>
<text v-if="userMobile" class="user-mobile">({{ userMobile }})</text>
<text v-if="starName" class="user-star">@ {{ starName }}</text>
</view>
</view>
<!-- 类型专属 body 由父级通过 slot 注入 -->
<slot name="body" />
<view v-if="!notification.is_read" class="postcard-unread-tip">
<text> 未读</text>
</view>
<!-- 取消 PostcardBase 自带 toolbar: detail.vue 底部 toolbar 重复
列表视图用 onLongPress 触发 ActionSheet(标已读/删除)代替 -->
</view>
</template>
<script setup>
import { computed } from 'vue'
import { parseData, typeLabel, formatTime } from './postcardHelpers.js'
const props = defineProps({
notification: { type: Object, required: true },
displayTitle: { type: String, default: '' },
})
// emit 仍保留以防父组件需要
defineEmits(['mark-read', 'delete'])
const data = computed(() => parseData(props.notification.data))
const typeLabelText = computed(() => typeLabel(props.notification.type))
const starName = computed(() => data.value.star_name || '')
const userMobile = computed(() => data.value.user_mobile || '')
const userNickname = computed(() => data.value.user_nickname || '')
function onMarkRead() { emit('mark-read', props.notification) }
function onDelete() { emit('delete', props.notification) }
</script>
<style lang="scss" scoped>
.postcard-base {
background: #fff;
border-radius: 16rpx;
padding: 32rpx;
.postcard-header {
border-bottom: 1rpx solid #f5f5f5;
padding-bottom: 16rpx;
margin-bottom: 16rpx;
}
.postcard-title {
font-size: 32rpx;
font-weight: 600;
display: block;
margin-bottom: 12rpx;
}
.postcard-meta {
display: flex;
gap: 16rpx;
font-size: 22rpx;
color: #999;
.meta-type { color: #5C40FF; }
.meta-star { color: #5C40FF; font-weight: 500; }
}
.postcard-user {
margin-top: 12rpx;
font-size: 24rpx;
color: #888;
.user-nick { margin-left: 16rpx; }
}
.postcard-unread-tip {
margin-top: 24rpx;
padding-top: 16rpx;
border-top: 1rpx solid #f5f5f5;
color: #f5222d;
font-size: 24rpx;
}
.postcard-toolbar {
margin-top: 24rpx;
padding-top: 16rpx;
border-top: 1rpx solid #f5f5f5;
display: flex;
gap: 16rpx;
background: #fafafa;
border-radius: 12rpx;
padding: 16rpx;
.toolbar-btn {
flex: 1;
text-align: center;
padding: 12rpx 0;
border-radius: 8rpx;
font-size: 26rpx;
&.toolbar-btn-read { background: #e8f4ff; color: #5C40FF; }
&.toolbar-btn-delete { background: #fff1f0; color: #f5222d; }
}
}
}
</style>