first
This commit is contained in:
500
user-app/src/pages/order/index.vue
Normal file
500
user-app/src/pages/order/index.vue
Normal file
@@ -0,0 +1,500 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { appApi, type OrderListItem } from "../../api/app";
|
||||
import { isLoggedIn, redirectToLogin } from "../../utils/auth";
|
||||
import { showErrorToast } from "../../utils/feedback";
|
||||
import { getPrivacyMode, maskOrderNo } from "../../utils/privacy";
|
||||
|
||||
const orders = ref<OrderListItem[]>([]);
|
||||
const privacyMode = ref(getPrivacyMode());
|
||||
const orderHeroBackground = ref("");
|
||||
const defaultOrderHeroBackground = "/static/order/order-reference.jpg";
|
||||
|
||||
const orderHeroStyle = computed(() => ({
|
||||
backgroundImage: `url("${orderHeroBackground.value || defaultOrderHeroBackground}")`,
|
||||
}));
|
||||
|
||||
const heroStats = computed(() => {
|
||||
const stats = {
|
||||
pending: 0,
|
||||
processing: 0,
|
||||
completed: 0,
|
||||
};
|
||||
|
||||
for (const item of orders.value) {
|
||||
if (["report_published", "completed"].includes(item.order_status)) {
|
||||
stats.completed += 1;
|
||||
continue;
|
||||
}
|
||||
if (item.order_status === "pending_supplement") {
|
||||
stats.pending += 1;
|
||||
continue;
|
||||
}
|
||||
stats.processing += 1;
|
||||
}
|
||||
|
||||
return stats;
|
||||
});
|
||||
|
||||
const emptyState = computed(() => ({
|
||||
title: "还没有鉴定订单",
|
||||
desc: "发起第一笔鉴定后,订单进度、补资料提醒和报告状态都会集中展示在这里。",
|
||||
}));
|
||||
|
||||
function openOrder(id: number) {
|
||||
uni.navigateTo({ url: `/pages/order/detail?id=${id}` });
|
||||
}
|
||||
|
||||
function goStartAppraisal() {
|
||||
uni.navigateTo({ url: "/pages/appraisal/service" });
|
||||
}
|
||||
|
||||
function goHome() {
|
||||
uni.switchTab({ url: "/pages/home/index" });
|
||||
}
|
||||
|
||||
function goHelp() {
|
||||
uni.navigateTo({ url: "/pages/help/index" });
|
||||
}
|
||||
|
||||
async function fetchPageVisuals() {
|
||||
try {
|
||||
const data = await appApi.getPageVisuals();
|
||||
orderHeroBackground.value = data.order_background_image_url || "";
|
||||
} catch (error) {
|
||||
console.warn("order page visuals fallback", error);
|
||||
}
|
||||
}
|
||||
|
||||
onShow(async () => {
|
||||
privacyMode.value = getPrivacyMode();
|
||||
void fetchPageVisuals();
|
||||
if (!isLoggedIn()) {
|
||||
orders.value = [];
|
||||
redirectToLogin("/pages/order/index");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await appApi.getOrders();
|
||||
orders.value = data.list;
|
||||
} catch (error) {
|
||||
orders.value = [];
|
||||
showErrorToast(error, "订单加载失败");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="order-page">
|
||||
<view :class="['order-hero', orders.length === 0 ? 'order-hero--empty' : 'order-hero--list']">
|
||||
<view class="order-hero__bg" :style="orderHeroStyle"></view>
|
||||
<view class="order-nav">
|
||||
<view class="order-nav__home" @click="goHome">
|
||||
<view class="order-nav__home-roof"></view>
|
||||
<view class="order-nav__home-body"></view>
|
||||
</view>
|
||||
<view class="order-nav__title">订单中心</view>
|
||||
<view class="order-nav__capsule">
|
||||
<text class="order-nav__dots">•••</text>
|
||||
<view class="order-nav__divider"></view>
|
||||
<view class="order-nav__circle"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="order-hero__content">
|
||||
<view class="order-hero__title">订单中心</view>
|
||||
<view class="order-hero__desc">查看每一笔鉴定从下单、寄送、补资料到出报告的完整进度。</view>
|
||||
|
||||
<view class="order-stat-grid">
|
||||
<view class="order-stat-card">
|
||||
<view class="order-stat-card__value">{{ heroStats.pending }}</view>
|
||||
<view class="order-stat-card__label">待处理</view>
|
||||
</view>
|
||||
<view class="order-stat-card">
|
||||
<view class="order-stat-card__value">{{ heroStats.processing }}</view>
|
||||
<view class="order-stat-card__label">进行中</view>
|
||||
</view>
|
||||
<view class="order-stat-card">
|
||||
<view class="order-stat-card__value">{{ heroStats.completed }}</view>
|
||||
<view class="order-stat-card__label">已完成</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="orders.length === 0" class="order-empty-state">
|
||||
<view class="order-empty-state__title">{{ emptyState.title }}</view>
|
||||
<view class="order-empty-state__desc">{{ emptyState.desc }}</view>
|
||||
<view class="order-empty-state__actions">
|
||||
<view class="order-empty-state__button order-empty-state__button--primary" @click="goStartAppraisal">发起鉴定</view>
|
||||
<view class="order-empty-state__button order-empty-state__button--secondary" @click="goHelp">查看帮助</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="order-list">
|
||||
<view
|
||||
v-for="item in orders"
|
||||
:key="item.order_id"
|
||||
class="order-card"
|
||||
@click="openOrder(item.order_id)"
|
||||
>
|
||||
<view class="order-card__top">
|
||||
<view>
|
||||
<view class="order-card__title">{{ item.product_name }}</view>
|
||||
<view class="order-card__no">订单号:{{ maskOrderNo(item.order_no, privacyMode) }}</view>
|
||||
</view>
|
||||
<text
|
||||
class="order-card__status"
|
||||
:class="item.order_status === 'pending_supplement' ? 'order-card__status--warning' : ['report_published', 'completed'].includes(item.order_status) ? 'order-card__status--success' : 'order-card__status--info'"
|
||||
>
|
||||
{{ item.display_status }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="order-card__desc">{{ item.status_desc }}</view>
|
||||
<view v-if="item.order_status === 'report_published'" class="order-card__desc">平台待安排寄回,请先确认寄回地址。</view>
|
||||
<view v-if="item.order_status === 'completed' && item.display_status === '物品已寄回'" class="order-card__desc">平台已回寄商品,请留意签收物流。</view>
|
||||
<view class="order-card__footer">
|
||||
<view class="order-card__provider">{{ item.service_provider === "zhongjian" ? "中检鉴定" : "安心验鉴定" }}</view>
|
||||
<view class="order-card__action">{{ item.primary_action }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-page {
|
||||
width: 100vw;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
|
||||
background: #f2f2f4;
|
||||
color: #2d2d2f;
|
||||
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.order-page,
|
||||
.order-page view,
|
||||
.order-page text {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.order-hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100vw;
|
||||
padding: 88rpx 32rpx 0;
|
||||
}
|
||||
|
||||
.order-hero--empty {
|
||||
height: 900rpx;
|
||||
}
|
||||
|
||||
.order-hero--list {
|
||||
height: 790rpx;
|
||||
}
|
||||
|
||||
.order-hero__bg {
|
||||
position: absolute;
|
||||
inset: -18rpx;
|
||||
background-size: 100vw auto;
|
||||
background-position: top center;
|
||||
background-repeat: no-repeat;
|
||||
filter: blur(8rpx) saturate(1.08);
|
||||
transform: scale(1.04);
|
||||
opacity: 0.82;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.order-hero::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(245, 239, 232, 0.08) 0%, rgba(243, 243, 245, 0.22) 42%, #f2f2f4 88%),
|
||||
linear-gradient(0deg, rgba(242, 242, 244, 0.36), rgba(255, 255, 255, 0.04));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.order-nav,
|
||||
.order-hero__content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.order-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 76rpx;
|
||||
}
|
||||
|
||||
.order-nav__title {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
color: #272729;
|
||||
font-size: 34rpx;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
transform: translate(-50%, -50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.order-nav__home {
|
||||
position: relative;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.order-nav__home-roof {
|
||||
position: absolute;
|
||||
left: 10rpx;
|
||||
top: 7rpx;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-top: 4rpx solid #2a2a2c;
|
||||
border-left: 4rpx solid #2a2a2c;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.order-nav__home-body {
|
||||
position: absolute;
|
||||
left: 12rpx;
|
||||
bottom: 8rpx;
|
||||
width: 34rpx;
|
||||
height: 30rpx;
|
||||
border: 4rpx solid #2a2a2c;
|
||||
border-top: 0;
|
||||
border-radius: 0 0 8rpx 8rpx;
|
||||
background: linear-gradient(135deg, transparent 0 36%, #f0c000 36% 100%);
|
||||
}
|
||||
|
||||
.order-nav__capsule {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
width: 174rpx;
|
||||
height: 64rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 34rpx;
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
box-shadow: 0 6rpx 18rpx rgba(0, 0, 0, 0.05);
|
||||
backdrop-filter: blur(12rpx);
|
||||
}
|
||||
|
||||
.order-nav__dots {
|
||||
color: #050505;
|
||||
font-size: 36rpx;
|
||||
line-height: 1;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.order-nav__divider {
|
||||
width: 1rpx;
|
||||
height: 34rpx;
|
||||
background: rgba(20, 20, 20, 0.68);
|
||||
}
|
||||
|
||||
.order-nav__circle {
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
border: 7rpx solid #070707;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.order-hero__content {
|
||||
margin-top: 214rpx;
|
||||
}
|
||||
|
||||
.order-hero__title {
|
||||
color: #262628;
|
||||
font-size: 70rpx;
|
||||
line-height: 1.08;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.order-hero__desc {
|
||||
width: 650rpx;
|
||||
max-width: 100%;
|
||||
margin-top: 28rpx;
|
||||
color: #666;
|
||||
font-size: 29rpx;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.order-stat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 42rpx;
|
||||
margin: 26rpx 22rpx 0;
|
||||
}
|
||||
|
||||
.order-stat-card {
|
||||
height: 176rpx;
|
||||
border-radius: 10rpx;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
text-align: center;
|
||||
box-shadow: 0 10rpx 24rpx rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.order-stat-card__value {
|
||||
margin-top: 35rpx;
|
||||
color: #2b2b2d;
|
||||
font-size: 68rpx;
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.order-stat-card__label {
|
||||
margin-top: 18rpx;
|
||||
color: #686868;
|
||||
font-size: 25rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.order-empty-state {
|
||||
margin: 190rpx 32rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.order-empty-state__title {
|
||||
color: #202022;
|
||||
font-size: 40rpx;
|
||||
line-height: 1.2;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.order-empty-state__desc {
|
||||
width: 560rpx;
|
||||
max-width: 100%;
|
||||
margin: 28rpx auto 0;
|
||||
color: #818181;
|
||||
font-size: 29rpx;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.order-empty-state__actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 38rpx;
|
||||
margin-top: 50rpx;
|
||||
}
|
||||
|
||||
.order-empty-state__button {
|
||||
height: 68rpx;
|
||||
border-radius: 36rpx;
|
||||
font-size: 25rpx;
|
||||
font-weight: 700;
|
||||
line-height: 68rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.order-empty-state__button--primary {
|
||||
background: #edbd00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.order-empty-state__button--secondary {
|
||||
background: #fff;
|
||||
color: #29292b;
|
||||
}
|
||||
|
||||
.order-list {
|
||||
display: grid;
|
||||
gap: 22rpx;
|
||||
margin: -22rpx 32rpx 0;
|
||||
padding-bottom: 36rpx;
|
||||
}
|
||||
|
||||
.order-card {
|
||||
padding: 28rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #fff;
|
||||
box-shadow: 0 12rpx 26rpx rgba(0, 0, 0, 0.035);
|
||||
}
|
||||
|
||||
.order-card__top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.order-card__title {
|
||||
color: #242426;
|
||||
font-size: 31rpx;
|
||||
line-height: 1.25;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.order-card__no,
|
||||
.order-card__desc {
|
||||
margin-top: 12rpx;
|
||||
color: #777;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.order-card__status {
|
||||
flex-shrink: 0;
|
||||
height: 42rpx;
|
||||
padding: 0 16rpx;
|
||||
border-radius: 22rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
|
||||
.order-card__status--warning {
|
||||
background: #fff4d9;
|
||||
color: #a97700;
|
||||
}
|
||||
|
||||
.order-card__status--success {
|
||||
background: #eaf7ef;
|
||||
color: #2d7652;
|
||||
}
|
||||
|
||||
.order-card__status--info {
|
||||
background: #edf4ff;
|
||||
color: #28669f;
|
||||
}
|
||||
|
||||
.order-card__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
|
||||
.order-card__provider {
|
||||
color: #9a9a9a;
|
||||
font-size: 23rpx;
|
||||
}
|
||||
|
||||
.order-card__action {
|
||||
min-width: 126rpx;
|
||||
height: 48rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 25rpx;
|
||||
background: #f1bd00;
|
||||
color: #fff;
|
||||
font-size: 23rpx;
|
||||
font-weight: 700;
|
||||
line-height: 48rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.order-stat-grid {
|
||||
gap: 22rpx;
|
||||
margin-left: 12rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.order-empty-state__actions {
|
||||
gap: 22rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user