559 lines
13 KiB
Vue
559 lines
13 KiB
Vue
<script setup lang="ts">
|
||
import { computed, ref } from "vue";
|
||
import { onShow } from "@dcloudio/uni-app";
|
||
import { appApi, type ReportListItem } from "../../api/app";
|
||
import { getPrivacyMode, maskOrderNo } from "../../utils/privacy";
|
||
import { showErrorToast } from "../../utils/feedback";
|
||
import { isLoggedIn, redirectToLogin } from "../../utils/auth";
|
||
|
||
const reports = ref<ReportListItem[]>([]);
|
||
const privacyMode = ref(getPrivacyMode());
|
||
const reportHeroBackground = ref("");
|
||
const defaultReportHeroBackground = "/static/report/report-reference.jpg";
|
||
|
||
const reportStats = computed(() => ({
|
||
total: reports.value.length,
|
||
published: reports.value.filter((item) => item.report_id).length,
|
||
pending: reports.value.filter((item) => !item.report_id).length,
|
||
}));
|
||
|
||
const reportHeroStyle = computed(() => ({
|
||
backgroundImage: `url("${reportHeroBackground.value || defaultReportHeroBackground}")`,
|
||
}));
|
||
|
||
const emptyState = computed(() => ({
|
||
title: "暂无鉴定报告",
|
||
desc: "正式报告生成后,您可以在这里查看结果、下载PDF,并继续进入验真。",
|
||
}));
|
||
|
||
function openReport(item: ReportListItem) {
|
||
if (!item.report_id || !item.report_no) {
|
||
uni.navigateTo({ url: `/pages/order/detail?id=${item.order_id}` });
|
||
return;
|
||
}
|
||
uni.navigateTo({ url: `/pages/report/detail?report_no=${encodeURIComponent(item.report_no)}` });
|
||
}
|
||
|
||
function goHome() {
|
||
uni.switchTab({ url: "/pages/home/index" });
|
||
}
|
||
|
||
function goStartAppraisal() {
|
||
uni.navigateTo({ url: "/pages/appraisal/service" });
|
||
}
|
||
|
||
function goHelp() {
|
||
uni.navigateTo({ url: "/pages/help/index" });
|
||
}
|
||
|
||
async function fetchPageVisuals() {
|
||
try {
|
||
const data = await appApi.getPageVisuals();
|
||
reportHeroBackground.value = data.report_background_image_url || "";
|
||
} catch (error) {
|
||
console.warn("report page visuals fallback", error);
|
||
}
|
||
}
|
||
|
||
onShow(async () => {
|
||
privacyMode.value = getPrivacyMode();
|
||
void fetchPageVisuals();
|
||
if (!isLoggedIn()) {
|
||
reports.value = [];
|
||
redirectToLogin("/pages/report/index");
|
||
return;
|
||
}
|
||
try {
|
||
const data = await appApi.getReports();
|
||
reports.value = data.list;
|
||
} catch (error) {
|
||
reports.value = [];
|
||
showErrorToast(error, "报告加载失败");
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<view class="report-page">
|
||
<view :class="['report-hero', reports.length === 0 ? 'report-hero--empty' : 'report-hero--list']">
|
||
<view class="report-hero__bg" :style="reportHeroStyle"></view>
|
||
<view class="report-nav">
|
||
<view class="report-nav__home" @click="goHome">
|
||
<view class="report-nav__home-roof"></view>
|
||
<view class="report-nav__home-body"></view>
|
||
</view>
|
||
<view class="report-nav__title">报告中心</view>
|
||
<view class="report-nav__capsule">
|
||
<text class="report-nav__dots">•••</text>
|
||
<view class="report-nav__divider"></view>
|
||
<view class="report-nav__circle"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="report-hero__content">
|
||
<view class="report-hero__title">报告中心</view>
|
||
<view class="report-hero__desc">正式报告会自动归档到这里,支持查看结果、下载 PDF 和继续验真。</view>
|
||
|
||
<view class="report-stat-grid">
|
||
<view class="report-stat-card">
|
||
<view class="report-stat-card__value">{{ reportStats.total }}</view>
|
||
<view class="report-stat-card__label">报告总数</view>
|
||
</view>
|
||
<view class="report-stat-card">
|
||
<view class="report-stat-card__value">{{ reportStats.published }}</view>
|
||
<view class="report-stat-card__label">已出报告</view>
|
||
</view>
|
||
<view class="report-stat-card">
|
||
<view class="report-stat-card__value">{{ reportStats.pending }}</view>
|
||
<view class="report-stat-card__label">待生成</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="reports.length === 0" class="report-empty-state">
|
||
<view class="report-empty-state__title">{{ emptyState.title }}</view>
|
||
<view class="report-empty-state__desc">{{ emptyState.desc }}</view>
|
||
<view class="report-empty-state__actions">
|
||
<view class="report-empty-state__button report-empty-state__button--primary" @click="goStartAppraisal">发起鉴定</view>
|
||
<view class="report-empty-state__button report-empty-state__button--secondary" @click="goHelp">查看帮助</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-else class="report-list">
|
||
<view
|
||
v-for="item in reports"
|
||
:key="item.report_id || item.order_id"
|
||
class="report-card"
|
||
@click="openReport(item)"
|
||
>
|
||
<view class="report-card__top">
|
||
<view class="report-card__thumb">
|
||
<image v-if="item.product_cover" class="report-card__image" :src="item.product_cover" mode="aspectFill" />
|
||
<view v-else class="report-card__placeholder">
|
||
<view class="report-card__placeholder-line"></view>
|
||
<view class="report-card__placeholder-search"></view>
|
||
</view>
|
||
</view>
|
||
<view class="report-card__content">
|
||
<view class="report-card__title">{{ item.product_name }}</view>
|
||
<view class="report-card__no">
|
||
报告编号:{{ item.report_no ? maskOrderNo(item.report_no, privacyMode) : "待生成" }}
|
||
</view>
|
||
<view class="report-card__institution">出具机构:{{ item.institution_name || "待确认" }}</view>
|
||
</view>
|
||
<text
|
||
class="report-card__status"
|
||
:class="item.report_id ? 'report-card__status--success' : 'report-card__status--info'"
|
||
>
|
||
{{ item.report_id ? "已出报告" : "待生成" }}
|
||
</text>
|
||
</view>
|
||
<view class="report-card__footer">
|
||
<view class="report-card__result">{{ item.result_text || "等待鉴定结果" }}</view>
|
||
<view class="report-card__action">{{ item.report_id ? "查看报告" : "查看订单" }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
.report-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;
|
||
}
|
||
|
||
.report-page,
|
||
.report-page view,
|
||
.report-page text {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.report-hero {
|
||
position: relative;
|
||
width: 100vw;
|
||
overflow: hidden;
|
||
padding: 88rpx 32rpx 0;
|
||
}
|
||
|
||
.report-hero--empty {
|
||
height: 900rpx;
|
||
}
|
||
|
||
.report-hero--list {
|
||
height: 900rpx;
|
||
}
|
||
|
||
.report-hero__bg {
|
||
position: absolute;
|
||
inset: -18rpx;
|
||
background-repeat: no-repeat;
|
||
background-position: top center;
|
||
background-size: 100vw auto;
|
||
filter: blur(8rpx) saturate(1.04);
|
||
opacity: 0.76;
|
||
transform: scale(1.04);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.report-hero::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
background:
|
||
linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, rgba(242, 242, 244, 0.18) 45%, rgba(242, 242, 244, 0.78) 76%, #f2f2f4 96%),
|
||
linear-gradient(0deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.06));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.report-nav,
|
||
.report-hero__content {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.report-nav {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 76rpx;
|
||
}
|
||
|
||
.report-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;
|
||
}
|
||
|
||
.report-nav__home {
|
||
position: relative;
|
||
width: 56rpx;
|
||
height: 56rpx;
|
||
}
|
||
|
||
.report-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);
|
||
}
|
||
|
||
.report-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%);
|
||
}
|
||
|
||
.report-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);
|
||
}
|
||
|
||
.report-nav__dots {
|
||
color: #050505;
|
||
font-size: 36rpx;
|
||
line-height: 1;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.report-nav__divider {
|
||
width: 1rpx;
|
||
height: 34rpx;
|
||
background: rgba(20, 20, 20, 0.68);
|
||
}
|
||
|
||
.report-nav__circle {
|
||
width: 38rpx;
|
||
height: 38rpx;
|
||
border: 7rpx solid #070707;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.report-hero__content {
|
||
margin-top: 260rpx;
|
||
}
|
||
|
||
.report-hero__title {
|
||
color: #262628;
|
||
font-size: 70rpx;
|
||
line-height: 1.08;
|
||
font-weight: 800;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.report-hero__desc {
|
||
width: 650rpx;
|
||
max-width: 100%;
|
||
margin-top: 28rpx;
|
||
color: #666;
|
||
font-size: 29rpx;
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.report-stat-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 42rpx;
|
||
margin: 26rpx 22rpx 0;
|
||
}
|
||
|
||
.report-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);
|
||
}
|
||
|
||
.report-stat-card__value {
|
||
margin-top: 35rpx;
|
||
color: #2b2b2d;
|
||
font-size: 68rpx;
|
||
line-height: 1;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.report-stat-card__label {
|
||
margin-top: 18rpx;
|
||
color: #686868;
|
||
font-size: 25rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
.report-empty-state {
|
||
margin: 240rpx 32rpx 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.report-empty-state__title {
|
||
color: #202022;
|
||
font-size: 40rpx;
|
||
line-height: 1.2;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.report-empty-state__desc {
|
||
width: 560rpx;
|
||
max-width: 100%;
|
||
margin: 28rpx auto 0;
|
||
color: #818181;
|
||
font-size: 29rpx;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.report-empty-state__actions {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 38rpx;
|
||
margin-top: 50rpx;
|
||
}
|
||
|
||
.report-empty-state__button {
|
||
min-width: 0;
|
||
height: 68rpx;
|
||
border-radius: 36rpx;
|
||
font-size: 25rpx;
|
||
font-weight: 700;
|
||
line-height: 68rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.report-empty-state__button--primary {
|
||
background: #edbd00;
|
||
color: #fff;
|
||
}
|
||
|
||
.report-empty-state__button--secondary {
|
||
background: #fff;
|
||
color: #29292b;
|
||
}
|
||
|
||
.report-list {
|
||
display: grid;
|
||
gap: 22rpx;
|
||
margin: -22rpx 32rpx 0;
|
||
padding-bottom: 36rpx;
|
||
}
|
||
|
||
.report-card {
|
||
padding: 24rpx;
|
||
border-radius: 16rpx;
|
||
background: #fff;
|
||
box-shadow: 0 12rpx 26rpx rgba(0, 0, 0, 0.035);
|
||
}
|
||
|
||
.report-card__top {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.report-card__thumb {
|
||
flex: 0 0 104rpx;
|
||
width: 104rpx;
|
||
height: 104rpx;
|
||
overflow: hidden;
|
||
border-radius: 12rpx;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
.report-card__image {
|
||
display: block;
|
||
width: 104rpx;
|
||
height: 104rpx;
|
||
}
|
||
|
||
.report-card__placeholder {
|
||
position: relative;
|
||
width: 104rpx;
|
||
height: 104rpx;
|
||
background: #f7f7f7;
|
||
}
|
||
|
||
.report-card__placeholder-line {
|
||
position: absolute;
|
||
left: 31rpx;
|
||
top: 22rpx;
|
||
width: 36rpx;
|
||
height: 52rpx;
|
||
border: 5rpx solid #2e2e30;
|
||
border-radius: 4rpx;
|
||
}
|
||
|
||
.report-card__placeholder-search {
|
||
position: absolute;
|
||
right: 18rpx;
|
||
bottom: 18rpx;
|
||
width: 34rpx;
|
||
height: 34rpx;
|
||
border: 5rpx solid #edbd00;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.report-card__placeholder-search::after {
|
||
content: "";
|
||
position: absolute;
|
||
right: -10rpx;
|
||
bottom: -6rpx;
|
||
width: 18rpx;
|
||
height: 5rpx;
|
||
border-radius: 4rpx;
|
||
background: #edbd00;
|
||
transform: rotate(45deg);
|
||
}
|
||
|
||
.report-card__content {
|
||
min-width: 0;
|
||
flex: 1;
|
||
}
|
||
|
||
.report-card__title {
|
||
color: #242426;
|
||
font-size: 31rpx;
|
||
line-height: 1.25;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.report-card__no,
|
||
.report-card__institution {
|
||
margin-top: 10rpx;
|
||
color: #777;
|
||
font-size: 23rpx;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.report-card__status {
|
||
flex-shrink: 0;
|
||
height: 42rpx;
|
||
padding: 0 16rpx;
|
||
border-radius: 22rpx;
|
||
font-size: 22rpx;
|
||
line-height: 42rpx;
|
||
}
|
||
|
||
.report-card__status--success {
|
||
background: #eaf7ef;
|
||
color: #2d7652;
|
||
}
|
||
|
||
.report-card__status--info {
|
||
background: #edf4ff;
|
||
color: #28669f;
|
||
}
|
||
|
||
.report-card__footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 18rpx;
|
||
margin-top: 24rpx;
|
||
}
|
||
|
||
.report-card__result {
|
||
min-width: 0;
|
||
color: #777;
|
||
font-size: 24rpx;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.report-card__action {
|
||
flex-shrink: 0;
|
||
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) {
|
||
.report-stat-grid {
|
||
gap: 22rpx;
|
||
margin-left: 12rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.report-empty-state__actions {
|
||
gap: 22rpx;
|
||
}
|
||
|
||
.report-card__top {
|
||
gap: 14rpx;
|
||
}
|
||
}
|
||
</style>
|