476 lines
10 KiB
Vue
476 lines
10 KiB
Vue
<template>
|
||
<view class="detail-page">
|
||
<view class="dream-bg dream-bg-one"></view>
|
||
<view class="dream-bg dream-bg-two"></view>
|
||
|
||
<view class="detail-header">
|
||
<view class="back-button" hover-class="button-hover" @tap="goBack">‹</view>
|
||
<view>
|
||
<text class="detail-title">短信详情</text>
|
||
<text class="detail-subtitle">查看这条匿名短信的发送进度</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="status-card" :class="statusMeta.className">
|
||
<view class="status-top">
|
||
<view class="status-pill" :class="statusMeta.className">
|
||
<text class="status-dot"></text>
|
||
<text>{{ statusMeta.text }}</text>
|
||
</view>
|
||
<text class="order-text">#{{ currentMail.orderNumber }}</text>
|
||
</view>
|
||
<text class="status-title">{{ statusTitle }}</text>
|
||
<text class="status-desc">{{ statusDesc }}</text>
|
||
</view>
|
||
|
||
<view class="phone-preview">
|
||
<view class="preview-header">
|
||
<view class="avatar">匿</view>
|
||
<view>
|
||
<text class="preview-name">匿名短信</text>
|
||
<text class="preview-recipient">发给 {{ maskedRecipient }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="imessage-area">
|
||
<view class="sms-bubble">
|
||
<text class="sms-content">{{ currentMail.content }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="info-panel">
|
||
<view class="info-row">
|
||
<text class="info-label">联系人</text>
|
||
<text class="info-value">{{ maskedRecipient }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">提交时间</text>
|
||
<text class="info-value">{{ submitTime }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">发送时间</text>
|
||
<text class="info-value">{{ sendTime }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">费用</text>
|
||
<text class="info-value">¥{{ currentMail.price.toFixed(2) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="timeline-panel">
|
||
<view class="timeline-item done">
|
||
<view class="timeline-dot"></view>
|
||
<view class="timeline-content">
|
||
<text class="timeline-title">已提交</text>
|
||
<text class="timeline-time">{{ submitTime }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="timeline-item" :class="{ done: currentMail.status === sentStatus, failed: currentMail.status === failedStatus }">
|
||
<view class="timeline-dot"></view>
|
||
<view class="timeline-content">
|
||
<text class="timeline-title">{{ currentMail.status === failedStatus ? "发送失败" : "等待发送" }}</text>
|
||
<text class="timeline-time">{{ sendTime }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, ref } from "vue";
|
||
import { onLoad } from "@dcloudio/uni-app";
|
||
|
||
import {
|
||
MAIL_STATUS_META,
|
||
MailStatus,
|
||
deserializeMail,
|
||
formatMailTime,
|
||
maskRecipient,
|
||
type Mail,
|
||
} from "@/config/mail";
|
||
|
||
const fallbackMail: Mail = {
|
||
id: 0,
|
||
orderNumber: "2503000000000000",
|
||
recipient: "13800138000",
|
||
content: "这是一条匿名短信记录,稍后会展示真实发送内容。",
|
||
sendTime: "2025-03-18 20:00:00",
|
||
submitTime: "2025-03-18 19:45:00",
|
||
status: MailStatus.PENDING,
|
||
price: 0,
|
||
isNew: false,
|
||
};
|
||
|
||
const mail = ref<Mail>(fallbackMail);
|
||
const sentStatus = MailStatus.SENT;
|
||
const failedStatus = MailStatus.FAILED;
|
||
|
||
onLoad((query) => {
|
||
const detail = deserializeMail(query?.mail as string | undefined);
|
||
|
||
if (detail) {
|
||
mail.value = detail;
|
||
}
|
||
});
|
||
|
||
const currentMail = computed(() => mail.value);
|
||
const statusMeta = computed(() => MAIL_STATUS_META[currentMail.value.status]);
|
||
const maskedRecipient = computed(() => maskRecipient(currentMail.value.recipient));
|
||
const submitTime = computed(() => formatMailTime(currentMail.value.submitTime));
|
||
const sendTime = computed(() => formatMailTime(currentMail.value.sendTime));
|
||
|
||
const statusTitle = computed(() => {
|
||
if (currentMail.value.status === MailStatus.SENT) return "短信已经送达";
|
||
if (currentMail.value.status === MailStatus.FAILED) return "发送没有成功";
|
||
return "短信正在等待发送";
|
||
});
|
||
|
||
const statusDesc = computed(() => {
|
||
if (currentMail.value.status === MailStatus.SENT) return "对方收到后,如果有回复会在信箱中展示。";
|
||
if (currentMail.value.status === MailStatus.FAILED) return "可以稍后重新提交,或检查联系人号码是否正确。";
|
||
return "系统会按预约时间发送,请留意后续状态变化。";
|
||
});
|
||
|
||
const goBack = () => {
|
||
uni.navigateBack({
|
||
delta: 1,
|
||
fail: () => {
|
||
uni.switchTab({
|
||
url: "/pages/mailbox/index",
|
||
});
|
||
},
|
||
});
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.detail-page {
|
||
min-height: 100vh;
|
||
box-sizing: border-box;
|
||
padding: 28rpx 26rpx 48rpx;
|
||
background:
|
||
linear-gradient(180deg, rgba(255, 244, 250, 0.98) 0%, rgba(238, 248, 255, 0.96) 48%, #f7f8fb 100%),
|
||
linear-gradient(135deg, rgba(255, 204, 225, 0.22), rgba(199, 233, 255, 0.24));
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.dream-bg {
|
||
position: fixed;
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
|
||
.dream-bg-one {
|
||
width: 360rpx;
|
||
height: 360rpx;
|
||
right: -160rpx;
|
||
top: 110rpx;
|
||
background: rgba(255, 188, 214, 0.24);
|
||
}
|
||
|
||
.dream-bg-two {
|
||
width: 320rpx;
|
||
height: 320rpx;
|
||
left: -140rpx;
|
||
top: 620rpx;
|
||
background: rgba(167, 218, 255, 0.22);
|
||
}
|
||
|
||
.detail-header,
|
||
.status-card,
|
||
.phone-preview,
|
||
.info-panel,
|
||
.timeline-panel {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.detail-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 18rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.back-button {
|
||
width: 68rpx;
|
||
height: 68rpx;
|
||
line-height: 62rpx;
|
||
text-align: center;
|
||
border-radius: 50%;
|
||
color: #293247;
|
||
font-size: 54rpx;
|
||
background: rgba(255, 255, 255, 0.78);
|
||
border: 1rpx solid rgba(255, 255, 255, 0.9);
|
||
box-shadow: 0 10rpx 24rpx rgba(83, 96, 130, 0.07);
|
||
}
|
||
|
||
.button-hover {
|
||
opacity: 0.72;
|
||
}
|
||
|
||
.detail-title,
|
||
.detail-subtitle,
|
||
.status-title,
|
||
.status-desc,
|
||
.preview-name,
|
||
.preview-recipient,
|
||
.sms-content,
|
||
.info-label,
|
||
.info-value,
|
||
.timeline-title,
|
||
.timeline-time {
|
||
display: block;
|
||
}
|
||
|
||
.detail-title {
|
||
color: #20273a;
|
||
font-size: 42rpx;
|
||
font-weight: 700;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.detail-subtitle {
|
||
margin-top: 8rpx;
|
||
color: #687286;
|
||
font-size: 24rpx;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.status-card {
|
||
overflow: hidden;
|
||
padding: 26rpx;
|
||
border-radius: 28rpx;
|
||
background:
|
||
linear-gradient(145deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 248, 252, 0.82) 55%, rgba(240, 249, 255, 0.86) 100%),
|
||
rgba(255, 255, 255, 0.78);
|
||
border: 1rpx solid rgba(255, 255, 255, 0.88);
|
||
box-shadow: 0 18rpx 42rpx rgba(93, 107, 139, 0.09);
|
||
}
|
||
|
||
.status-top {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 20rpx;
|
||
margin-bottom: 26rpx;
|
||
}
|
||
|
||
.status-pill {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 48rpx;
|
||
padding: 0 18rpx;
|
||
border-radius: 999rpx;
|
||
color: #fff;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.status-pill.status-pending {
|
||
background: linear-gradient(135deg, #ffb75e 0%, #f59e0b 100%);
|
||
}
|
||
|
||
.status-pill.status-sent {
|
||
background: linear-gradient(135deg, #7ee0a3 0%, #34b878 100%);
|
||
}
|
||
|
||
.status-pill.status-failed {
|
||
background: linear-gradient(135deg, #ff8daf 0%, #e85b7f 100%);
|
||
}
|
||
|
||
.status-dot {
|
||
width: 10rpx;
|
||
height: 10rpx;
|
||
margin-right: 10rpx;
|
||
border-radius: 50%;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
}
|
||
|
||
.order-text {
|
||
flex: 1;
|
||
color: #9aa3b2;
|
||
font-size: 22rpx;
|
||
text-align: right;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.status-title {
|
||
color: #263044;
|
||
font-size: 36rpx;
|
||
font-weight: 700;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.status-desc {
|
||
margin-top: 12rpx;
|
||
color: #687286;
|
||
font-size: 25rpx;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.phone-preview,
|
||
.info-panel,
|
||
.timeline-panel {
|
||
margin-top: 22rpx;
|
||
border-radius: 28rpx;
|
||
background: rgba(255, 255, 255, 0.78);
|
||
border: 1rpx solid rgba(255, 255, 255, 0.9);
|
||
box-shadow: 0 18rpx 42rpx rgba(93, 107, 139, 0.08);
|
||
}
|
||
|
||
.phone-preview {
|
||
overflow: hidden;
|
||
}
|
||
|
||
.preview-header {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 26rpx 28rpx 18rpx;
|
||
border-bottom: 1rpx solid rgba(230, 236, 246, 0.78);
|
||
}
|
||
|
||
.avatar {
|
||
width: 74rpx;
|
||
height: 74rpx;
|
||
line-height: 74rpx;
|
||
margin-right: 16rpx;
|
||
text-align: center;
|
||
border-radius: 50%;
|
||
color: #fff;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
background: linear-gradient(135deg, #ff78a2 0%, #83c6ff 100%);
|
||
}
|
||
|
||
.preview-name {
|
||
color: #263044;
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.preview-recipient {
|
||
margin-top: 6rpx;
|
||
color: #8c96a8;
|
||
font-size: 23rpx;
|
||
}
|
||
|
||
.imessage-area {
|
||
padding: 28rpx;
|
||
background: linear-gradient(180deg, rgba(247, 250, 255, 0.76), rgba(255, 248, 252, 0.72));
|
||
}
|
||
|
||
.sms-bubble {
|
||
max-width: 590rpx;
|
||
padding: 22rpx 24rpx;
|
||
border-radius: 28rpx 28rpx 28rpx 8rpx;
|
||
background: #fff;
|
||
border: 1rpx solid rgba(232, 238, 248, 0.88);
|
||
box-shadow: 0 10rpx 24rpx rgba(93, 107, 139, 0.06);
|
||
}
|
||
|
||
.sms-content {
|
||
color: #333b4f;
|
||
font-size: 29rpx;
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.info-panel {
|
||
padding: 8rpx 26rpx;
|
||
}
|
||
|
||
.info-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 24rpx;
|
||
min-height: 82rpx;
|
||
border-bottom: 1rpx solid rgba(230, 236, 246, 0.72);
|
||
}
|
||
|
||
.info-row:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.info-label {
|
||
color: #8c96a8;
|
||
font-size: 24rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.info-value {
|
||
color: #263044;
|
||
font-size: 25rpx;
|
||
font-weight: 600;
|
||
text-align: right;
|
||
}
|
||
|
||
.timeline-panel {
|
||
padding: 26rpx 28rpx;
|
||
}
|
||
|
||
.timeline-item {
|
||
position: relative;
|
||
display: flex;
|
||
padding-bottom: 30rpx;
|
||
}
|
||
|
||
.timeline-item:last-child {
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.timeline-item::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 11rpx;
|
||
top: 28rpx;
|
||
bottom: 0;
|
||
width: 2rpx;
|
||
background: rgba(200, 211, 226, 0.7);
|
||
}
|
||
|
||
.timeline-item:last-child::after {
|
||
display: none;
|
||
}
|
||
|
||
.timeline-dot {
|
||
width: 24rpx;
|
||
height: 24rpx;
|
||
margin-top: 4rpx;
|
||
margin-right: 20rpx;
|
||
border-radius: 50%;
|
||
background: #d5dde9;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.timeline-item.done .timeline-dot {
|
||
background: linear-gradient(135deg, #7ee0a3, #34b878);
|
||
}
|
||
|
||
.timeline-item.failed .timeline-dot {
|
||
background: linear-gradient(135deg, #ff8daf, #e85b7f);
|
||
}
|
||
|
||
.timeline-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.timeline-title {
|
||
color: #263044;
|
||
font-size: 27rpx;
|
||
font-weight: 700;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.timeline-time {
|
||
margin-top: 8rpx;
|
||
color: #8c96a8;
|
||
font-size: 23rpx;
|
||
}
|
||
</style>
|