110 lines
3.2 KiB
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> |