feat: update report detail presentation

This commit is contained in:
wushumin
2026-05-27 16:24:23 +08:00
parent 205849061d
commit 1f1f50843e
2 changed files with 599 additions and 192 deletions

View File

@@ -5,9 +5,23 @@ import { appApi, type EvidenceAttachmentAsset, type ReportDetailData } from "../
import { reportDetailFallback } from "../../mocks/app"; import { reportDetailFallback } from "../../mocks/app";
import { resolveErrorMessage } from "../../utils/feedback"; import { resolveErrorMessage } from "../../utils/feedback";
type ReportTab = "product" | "trace"; type ReportTab = "product" | "center" | "trace";
type ProductDisplayItem = ReportDetailData["product_display"]["items"][number]; type ProductDisplayItem = ReportDetailData["product_display"]["items"][number];
const centerIntroParagraphs = [
"安心验(深圳)商品检验鉴定有限责任公司立足深港核心产业服务区,是一家专业从事商品检验、鉴定、测试及技术咨询的第三方服务机构。",
"公司依托粤港澳大湾区雄厚的产业基础与国际贸易枢纽优势,致力于为 C 端消费者及 B 端电商平台、商家提供网购商品真伪鉴定、成色评估、价值评估及争议仲裁等一站式解决方案。",
];
const institutionBannerTitle = "安心验(深圳)商品检验鉴定有限责任公司";
const institutionBannerSubtitle = "ANXINYAN (SHENZHEN) COMMODITY INSPECTION AND APPRAISAL CO., LTD.";
const anxinyanStatementItems = [
"本电子意见书鉴定结果仅对送检样品负责,整体任一部分损毁或丢失则意见书失效。",
"若对意见书的内容和结论持有异议需在意见书出具后15日内提出逾期不予受理本电子意见书不得用于司法用途。",
"品牌方为商品的设计及制造方,如品牌方确认该鉴定样品为品牌方制造及销售商品,以品牌方的结论为准。",
"如因我司鉴定结果失误给申请人造成损失且无法挽回的,我司予以赔偿。赔偿范围仅限于申请人的直接损失,且赔偿金额总计不超过该检验项目鉴定费用的三倍。",
"企业信息和产品信息均由委托单位提供,鉴定结论不涉及样品品质检测等信息。",
];
const detail = ref<ReportDetailData>(reportDetailFallback); const detail = ref<ReportDetailData>(reportDetailFallback);
const downloading = ref(false); const downloading = ref(false);
const loading = ref(false); const loading = ref(false);
@@ -92,6 +106,11 @@ const productSpecItems = computed(() => {
].filter((item) => item.value && item.value !== "-"); ].filter((item) => item.value && item.value !== "-");
}); });
const traceInfoVisible = computed(() => Boolean(detail.value.trace_info?.visible || detail.value.report_header.trace_info_visible)); const traceInfoVisible = computed(() => Boolean(detail.value.trace_info?.visible || detail.value.report_header.trace_info_visible));
const centerTabVisible = computed(() => {
const serviceProvider = textValue(detail.value.report_header.service_provider).toLowerCase();
const serviceProviderTextValue = textValue(detail.value.report_header.service_provider_text);
return serviceProvider !== "zhongjian" && serviceProviderTextValue !== "中检鉴定";
});
const traceNodes = computed(() => (traceInfoVisible.value ? detail.value.trace_info?.nodes || [] : [])); const traceNodes = computed(() => (traceInfoVisible.value ? detail.value.trace_info?.nodes || [] : []));
const zhongjianReportFiles = computed(() => detail.value.zhongjian_report_files || []); const zhongjianReportFiles = computed(() => detail.value.zhongjian_report_files || []);
const zhongjianImageFiles = computed(() => zhongjianReportFiles.value.filter((item) => item.file_type === "image")); const zhongjianImageFiles = computed(() => zhongjianReportFiles.value.filter((item) => item.file_type === "image"));
@@ -206,12 +225,6 @@ function openAsset(item: EvidenceAttachmentAsset, files: EvidenceAttachmentAsset
uni.showToast({ title: "当前附件类型暂不支持预览", icon: "none" }); uni.showToast({ title: "当前附件类型暂不支持预览", icon: "none" });
} }
function contactService() {
uni.navigateTo({
url: `/pages/support/create?ticket_type=report_issue&prefill_title=${encodeURIComponent("报告咨询")}`,
});
}
function openAntiModal() { function openAntiModal() {
antiCode.value = ""; antiCode.value = "";
antiResult.value = null; antiResult.value = null;
@@ -306,8 +319,8 @@ function downloadPdf() {
}); });
} }
watch(traceInfoVisible, (visible) => { watch([traceInfoVisible, centerTabVisible], ([traceVisible, centerVisible]) => {
if (!visible && activeTab.value === "trace") { if ((!traceVisible && activeTab.value === "trace") || (!centerVisible && activeTab.value === "center")) {
activeTab.value = "product"; activeTab.value = "product";
} }
}); });
@@ -364,6 +377,13 @@ onLoad(async (options) => {
<template v-else> <template v-else>
<view class="report-shell"> <view class="report-shell">
<view class="report-shell__watermark" aria-hidden="true"></view> <view class="report-shell__watermark" aria-hidden="true"></view>
<view class="report-institution-banner">
<image class="report-institution-banner__logo" src="/static/logo.png" mode="aspectFit" />
<view class="report-institution-banner__text">
<view class="report-institution-banner__title">{{ institutionBannerTitle }}</view>
<view class="report-institution-banner__subtitle">{{ institutionBannerSubtitle }}</view>
</view>
</view>
<view class="report-cover"> <view class="report-cover">
<swiper v-if="reportImages.length" class="report-cover__swiper" indicator-dots circular> <swiper v-if="reportImages.length" class="report-cover__swiper" indicator-dots circular>
<swiper-item v-for="item in reportImages" :key="item.file_url || item.file_id"> <swiper-item v-for="item in reportImages" :key="item.file_url || item.file_id">
@@ -378,28 +398,29 @@ onLoad(async (options) => {
<view v-else class="report-cover__empty">暂无鉴定图片</view> <view v-else class="report-cover__empty">暂无鉴定图片</view>
</view> </view>
<view class="report-meta"> <view class="report-product-summary">
<view class="report-meta__row"> <view class="report-product-summary__row">
<text class="report-meta__label">产品名称</text> <text class="report-product-summary__tag">产品名称</text>
<text class="report-meta__value">{{ productName }}</text> <text class="report-product-summary__value">{{ productName }}</text>
</view> </view>
<view class="report-meta__row"> <view class="report-product-summary__row">
<text class="report-meta__label">检测机构</text> <text class="report-product-summary__tag report-product-summary__tag--muted">鉴定机构</text>
<text class="report-meta__value">{{ institutionName }}</text> <text class="report-product-summary__value">{{ institutionName }}</text>
</view> </view>
<view class="report-meta__row"> <view class="report-product-summary__row">
<text class="report-meta__label">报告编号</text> <text class="report-product-summary__tag report-product-summary__tag--muted">报告编号</text>
<text class="report-meta__value">{{ reportNo || "-" }}</text> <text class="report-product-summary__value">{{ reportNo || "-" }}</text>
</view> </view>
<view class="report-meta__row report-meta__row--date"> <view class="report-product-summary__row">
<text class="report-meta__label">出具日期</text> <text class="report-product-summary__tag report-product-summary__tag--muted">出具日期</text>
<text class="report-meta__download" @click="downloadPdf">{{ downloading ? "下载中" : "下载PDF" }}</text> <text class="report-product-summary__value">{{ publishTime }}</text>
<text class="report-meta__value">{{ publishTime }}</text> <text class="report-product-summary__download" @click="downloadPdf">{{ downloading ? "下载中" : "下载PDF" }}</text>
</view> </view>
</view> </view>
<view class="report-tabs"> <view class="report-tabs">
<view :class="['report-tab', activeTab === 'product' ? 'report-tab--active' : '']" @click="activeTab = 'product'">产品信息</view> <view :class="['report-tab', activeTab === 'product' ? 'report-tab--active' : '']" @click="activeTab = 'product'">产品信息</view>
<view v-if="centerTabVisible" :class="['report-tab', activeTab === 'center' ? 'report-tab--active' : '']" @click="activeTab = 'center'">鉴定中心</view>
<view v-if="traceInfoVisible" :class="['report-tab', activeTab === 'trace' ? 'report-tab--active' : '']" @click="activeTab = 'trace'">追溯信息</view> <view v-if="traceInfoVisible" :class="['report-tab', activeTab === 'trace' ? 'report-tab--active' : '']" @click="activeTab = 'trace'">追溯信息</view>
</view> </view>
@@ -446,9 +467,34 @@ onLoad(async (options) => {
</view> </view>
</view> </view>
</view> </view>
<view class="report-statement">
<view class="report-statement__title">安心验声明:</view>
<view v-for="(item, index) in anxinyanStatementItems" :key="item" class="report-statement__item">
{{ index + 1 }}{{ item }}
</view>
</view>
</view> </view>
<view v-else-if="traceInfoVisible" class="report-panel report-panel--trace"> <view v-else-if="activeTab === 'center' && centerTabVisible" class="report-panel report-panel--center">
<view class="center-profile">
<view class="center-profile__brand">
<image class="center-profile__logo" src="/static/logo.png" mode="aspectFit" />
<view class="center-profile__text">
<view class="center-profile__name">{{ institutionBannerTitle }}</view>
<view class="center-profile__subtitle">{{ institutionBannerSubtitle }}</view>
</view>
</view>
<view class="center-profile__divider"></view>
<view class="center-profile__body">
<view v-for="item in centerIntroParagraphs" :key="item" class="center-profile__paragraph">
{{ item }}
</view>
</view>
</view>
</view>
<view v-else-if="activeTab === 'trace' && traceInfoVisible" class="report-panel report-panel--trace">
<view v-if="traceNodes.length === 0" class="trace-empty">暂无追溯信息</view> <view v-if="traceNodes.length === 0" class="trace-empty">暂无追溯信息</view>
<view v-for="node in traceNodes" :key="node.code" class="trace-node"> <view v-for="node in traceNodes" :key="node.code" class="trace-node">
<view class="trace-node__head"> <view class="trace-node__head">
@@ -481,7 +527,6 @@ onLoad(async (options) => {
</view> </view>
<view class="fixed-action-bar report-actions"> <view class="fixed-action-bar report-actions">
<view class="btn btn--secondary" @click="contactService">联系我们</view>
<view class="btn btn--primary" @click="openAntiModal">防伪查询</view> <view class="btn btn--primary" @click="openAntiModal">防伪查询</view>
</view> </view>
@@ -542,23 +587,24 @@ onLoad(async (options) => {
max-width: 100vw; max-width: 100vw;
box-sizing: border-box; box-sizing: border-box;
overflow-x: hidden; overflow-x: hidden;
padding: 28rpx 32rpx 170rpx; padding: 0 0 150rpx;
background: #f1f3f6; background: #eef1f5;
color: #3c3f45; color: #3c3f45;
} }
.report-shell { .report-shell {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-radius: 28rpx; width: 100%;
border-radius: 0;
background: #ffffff; background: #ffffff;
box-shadow: 0 18rpx 48rpx rgba(31, 36, 48, 0.08); box-shadow: none;
} }
.report-shell__watermark { .report-shell__watermark {
position: absolute; position: absolute;
z-index: 0; z-index: 0;
top: 374rpx; top: 530rpx;
left: 28rpx; left: 28rpx;
right: 28rpx; right: 28rpx;
height: 560rpx; height: 560rpx;
@@ -568,18 +614,63 @@ onLoad(async (options) => {
} }
.report-cover, .report-cover,
.report-meta, .report-institution-banner,
.report-product-summary,
.report-tabs, .report-tabs,
.report-panel { .report-panel {
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.report-cover { .report-institution-banner {
height: 356rpx; display: flex;
margin: 28rpx 28rpx 0; align-items: center;
gap: 14rpx;
min-height: 124rpx;
padding: 0 28rpx;
border-bottom: 1px solid rgba(229, 229, 229, 0.7);
background: #ffffff;
}
.report-institution-banner__logo {
flex: 0 0 auto;
display: block;
width: 66rpx;
height: 66rpx;
}
.report-institution-banner__text {
flex: 1;
min-width: 0;
}
.report-institution-banner__title {
color: #34363b;
font-size: 21rpx;
font-weight: 900;
line-height: 1.24;
white-space: nowrap;
overflow: hidden; overflow: hidden;
border-radius: 10rpx; text-overflow: ellipsis;
}
.report-institution-banner__subtitle {
margin-top: 5rpx;
color: #737982;
font-size: 13rpx;
font-weight: 700;
line-height: 1.25;
letter-spacing: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.report-cover {
height: 412rpx;
margin: 0;
overflow: hidden;
border-radius: 0;
background: #e8eaee; background: #e8eaee;
} }
@@ -602,78 +693,89 @@ onLoad(async (options) => {
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
} }
.report-meta {
padding: 34rpx 28rpx 12rpx;
}
.report-meta__row {
display: flex;
align-items: flex-start;
gap: 18rpx;
min-width: 0;
min-height: 48rpx;
}
.report-meta__row + .report-meta__row {
margin-top: 20rpx;
}
.report-meta__label {
flex: 0 0 138rpx;
display: block;
color: #7d828a;
font-size: 28rpx;
line-height: 1.4;
}
.report-meta__value {
flex: 1;
display: block;
min-width: 0;
color: #44474d;
font-size: 28rpx;
font-weight: 700;
line-height: 1.35;
text-align: right;
white-space: normal;
word-break: break-all;
overflow-wrap: anywhere;
}
.report-meta__row--date .report-meta__value {
flex: 0 1 auto;
margin-left: auto;
word-break: normal;
}
.report-meta__download {
flex: 0 0 auto;
display: block;
min-height: 38rpx;
padding: 0 16rpx;
border: 1px solid rgba(221, 179, 47, 0.74);
border-radius: 6rpx;
background: #fff9e6;
color: var(--color-accent);
font-size: 22rpx;
font-weight: 700;
line-height: 36rpx;
}
.report-tabs { .report-tabs {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 104rpx; padding: 24rpx 20rpx 34rpx;
padding: 26rpx 40rpx 34rpx; border-top: 1px solid rgba(229, 229, 229, 0.72);
border-bottom: 1px solid rgba(229, 229, 229, 0.72);
}
.report-product-summary {
display: grid;
gap: 14rpx;
padding: 20rpx 28rpx 22rpx;
background: #e7e9ef;
}
.report-product-summary__row {
display: flex;
align-items: center;
gap: 14rpx;
min-width: 0;
}
.report-product-summary__tag {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 36rpx;
padding: 0 14rpx;
border: 1px solid rgba(237, 189, 0, 0.78);
border-radius: 6rpx;
color: var(--color-accent);
font-size: 22rpx;
font-weight: 800;
line-height: 1;
background: rgba(255, 255, 255, 0.62);
white-space: nowrap;
}
.report-product-summary__tag--muted {
border-color: #b8bdc6;
color: #5d636d;
}
.report-product-summary__value {
flex: 1;
min-width: 0;
color: #2f333a;
font-size: 24rpx;
font-weight: 800;
line-height: 1.45;
word-break: break-all;
overflow-wrap: anywhere;
}
.report-product-summary__download {
flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 34rpx;
padding: 0 12rpx;
border: 1px solid rgba(237, 189, 0, 0.78);
border-radius: 6rpx;
color: var(--color-accent);
font-size: 21rpx;
font-weight: 800;
line-height: 1;
background: rgba(255, 255, 255, 0.68);
white-space: nowrap;
} }
.report-tab { .report-tab {
position: relative; position: relative;
flex: 1;
min-width: 0;
color: #8e9298; color: #8e9298;
font-size: 32rpx; font-size: 28rpx;
font-weight: 700; font-weight: 700;
line-height: 1.6; line-height: 1.6;
text-align: center;
white-space: nowrap;
} }
.report-tab--active { .report-tab--active {
@@ -817,6 +919,99 @@ onLoad(async (options) => {
font-weight: 800; font-weight: 800;
} }
.report-statement {
position: relative;
z-index: 1;
margin-top: 32rpx;
padding-top: 10rpx;
color: #343840;
}
.report-statement__title {
font-size: 28rpx;
font-weight: 900;
line-height: 1.7;
}
.report-statement__item {
margin-top: 16rpx;
color: #3f444c;
font-size: 26rpx;
font-weight: 700;
line-height: 2;
word-break: break-all;
overflow-wrap: anywhere;
}
.report-panel--center {
padding-top: 28rpx;
}
.center-profile {
position: relative;
z-index: 1;
}
.center-profile__brand {
display: flex;
align-items: center;
gap: 18rpx;
min-width: 0;
}
.center-profile__logo {
flex: 0 0 auto;
display: block;
width: 72rpx;
height: 72rpx;
}
.center-profile__text {
flex: 1;
min-width: 0;
}
.center-profile__name {
color: #30333a;
font-size: 26rpx;
font-weight: 900;
line-height: 1.34;
word-break: break-all;
overflow-wrap: anywhere;
}
.center-profile__subtitle {
margin-top: 6rpx;
color: #66707c;
font-size: 18rpx;
font-weight: 700;
line-height: 1.25;
letter-spacing: 0;
word-break: break-all;
overflow-wrap: anywhere;
}
.center-profile__divider {
width: 100%;
height: 1px;
margin: 26rpx 0 24rpx;
background: #e5e5e5;
}
.center-profile__body {
display: grid;
gap: 18rpx;
}
.center-profile__paragraph {
color: #767b84;
font-size: 26rpx;
line-height: 1.82;
text-align: justify;
word-break: break-all;
overflow-wrap: anywhere;
}
.report-panel--trace { .report-panel--trace {
padding-top: 2rpx; padding-top: 2rpx;
} }
@@ -964,34 +1159,27 @@ onLoad(async (options) => {
} }
.report-actions { .report-actions {
display: grid; display: block;
grid-template-columns: 1fr 1fr;
gap: 28rpx;
width: 100vw; width: 100vw;
box-sizing: border-box; box-sizing: border-box;
padding: 22rpx 32rpx calc(22rpx + env(safe-area-inset-bottom)); padding: 0 0 env(safe-area-inset-bottom);
background: rgba(241, 243, 246, 0.96); background: #ffffff;
border-top: 0; border-top: 0;
box-shadow: 0 -10rpx 34rpx rgba(31, 36, 48, 0.05);
}
.report-actions .btn {
min-width: 0;
min-height: 76rpx;
border-radius: 999rpx;
font-size: 28rpx;
font-weight: 700;
box-shadow: none; box-shadow: none;
} }
.report-actions .btn--secondary { .report-actions .btn {
border: 0; width: 100%;
background: #ffffff; min-width: 0;
color: #4f535a; min-height: 112rpx;
border-radius: 0;
font-size: 32rpx;
font-weight: 800;
box-shadow: none;
} }
.report-actions .btn--primary { .report-actions .btn--primary {
background: #dfb733; background: #edbd00;
color: #ffffff; color: #ffffff;
} }

View File

@@ -1,10 +1,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from "vue"; import { computed, ref, watch } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app"; import { onLoad, onShow } from "@dcloudio/uni-app";
import { adminApi, type AdminFileAsset, type AdminReportDetail } from "../../api/admin"; import { adminApi, type AdminFileAsset, type AdminReportDetail } from "../../api/admin";
import { showErrorToast, showInfoToast } from "../../utils/feedback"; import { showErrorToast, showInfoToast } from "../../utils/feedback";
type ReportTab = "product" | "attachments"; type ReportTab = "product" | "center" | "attachments";
const centerIntroParagraphs = [
"安心验(深圳)商品检验鉴定有限责任公司立足深港核心产业服务区,是一家专业从事商品检验、鉴定、测试及技术咨询的第三方服务机构。",
"公司依托粤港澳大湾区雄厚的产业基础与国际贸易枢纽优势,致力于为 C 端消费者及 B 端电商平台、商家提供网购商品真伪鉴定、成色评估、价值评估及争议仲裁等一站式解决方案。",
];
const institutionBannerTitle = "安心验(深圳)商品检验鉴定有限责任公司";
const institutionBannerSubtitle = "ANXINYAN (SHENZHEN) COMMODITY INSPECTION AND APPRAISAL CO., LTD.";
const anxinyanStatementItems = [
"本电子意见书鉴定结果仅对送检样品负责,整体任一部分损毁或丢失则意见书失效。",
"若对意见书的内容和结论持有异议需在意见书出具后15日内提出逾期不予受理本电子意见书不得用于司法用途。",
"品牌方为商品的设计及制造方,如品牌方确认该鉴定样品为品牌方制造及销售商品,以品牌方的结论为准。",
"如因我司鉴定结果失误给申请人造成损失且无法挽回的,我司予以赔偿。赔偿范围仅限于申请人的直接损失,且赔偿金额总计不超过该检验项目鉴定费用的三倍。",
"企业信息和产品信息均由委托单位提供,鉴定结论不涉及样品品质检测等信息。",
];
const loading = ref(false); const loading = ref(false);
const pageReady = ref(false); const pageReady = ref(false);
@@ -16,7 +30,12 @@ const returnConfirming = ref(false);
const activeTab = ref<ReportTab>("product"); const activeTab = ref<ReportTab>("product");
const activeVideo = ref<AdminFileAsset | null>(null); const activeVideo = ref<AdminFileAsset | null>(null);
const isZhongjian = computed(() => detail.value?.report_header.service_provider === "zhongjian"); const isZhongjian = computed(() => {
const serviceProvider = textValue(detail.value?.report_header.service_provider).toLowerCase();
const serviceProviderTextValue = textValue(detail.value?.report_header.service_provider_text);
return serviceProvider === "zhongjian" || serviceProviderTextValue === "中检鉴定";
});
const centerTabVisible = computed(() => !isZhongjian.value);
const isReturnReview = computed(() => Boolean(returnInternalTagNo.value && reportId.value)); const isReturnReview = computed(() => Boolean(returnInternalTagNo.value && reportId.value));
const evidenceAttachments = computed(() => detail.value?.evidence_attachments || []); const evidenceAttachments = computed(() => detail.value?.evidence_attachments || []);
const zhongjianReportFiles = computed(() => isZhongjian.value ? (detail.value?.zhongjian_report_files || []) : []); const zhongjianReportFiles = computed(() => isZhongjian.value ? (detail.value?.zhongjian_report_files || []) : []);
@@ -246,6 +265,12 @@ async function confirmReturnFromReport() {
} }
} }
watch(centerTabVisible, (visible) => {
if (!visible && activeTab.value === "center") {
activeTab.value = "product";
}
});
onLoad((options) => { onLoad((options) => {
reportId.value = Number(options?.id || 0); reportId.value = Number(options?.id || 0);
returnInternalTagNo.value = readQueryString(options?.return_internal_tag_no); returnInternalTagNo.value = readQueryString(options?.return_internal_tag_no);
@@ -283,6 +308,13 @@ onShow(() => {
<view class="report-shell"> <view class="report-shell">
<view class="report-shell__watermark" aria-hidden="true"></view> <view class="report-shell__watermark" aria-hidden="true"></view>
<view class="report-institution-banner">
<image class="report-institution-banner__logo" src="/static/logo.png" mode="aspectFit" />
<view class="report-institution-banner__text">
<view class="report-institution-banner__title">{{ institutionBannerTitle }}</view>
<view class="report-institution-banner__subtitle">{{ institutionBannerSubtitle }}</view>
</view>
</view>
<view class="report-cover"> <view class="report-cover">
<swiper v-if="reportImages.length" class="report-cover__swiper" indicator-dots circular> <swiper v-if="reportImages.length" class="report-cover__swiper" indicator-dots circular>
<swiper-item v-for="item in reportImages" :key="item.file_url || item.file_id"> <swiper-item v-for="item in reportImages" :key="item.file_url || item.file_id">
@@ -297,28 +329,29 @@ onShow(() => {
<view v-else class="report-cover__empty">暂无鉴定图片</view> <view v-else class="report-cover__empty">暂无鉴定图片</view>
</view> </view>
<view class="report-meta"> <view class="report-product-summary">
<view class="report-meta__row"> <view class="report-product-summary__row">
<text class="report-meta__label">产品名称</text> <text class="report-product-summary__tag">产品名称</text>
<text class="report-meta__value">{{ productName }}</text> <text class="report-product-summary__value">{{ productName }}</text>
</view> </view>
<view class="report-meta__row"> <view class="report-product-summary__row">
<text class="report-meta__label">检测机构</text> <text class="report-product-summary__tag report-product-summary__tag--muted">鉴定机构</text>
<text class="report-meta__value">{{ institutionName }}</text> <text class="report-product-summary__value">{{ institutionName }}</text>
</view> </view>
<view class="report-meta__row"> <view class="report-product-summary__row">
<text class="report-meta__label">报告编号</text> <text class="report-product-summary__tag report-product-summary__tag--muted">报告编号</text>
<text class="report-meta__value">{{ reportNo }}</text> <text class="report-product-summary__value">{{ reportNo }}</text>
</view> </view>
<view class="report-meta__row report-meta__row--date"> <view class="report-product-summary__row">
<text class="report-meta__label">出具日期</text> <text class="report-product-summary__tag report-product-summary__tag--muted">出具日期</text>
<text class="report-meta__tag">{{ detail.report_header.report_status_text }}</text> <text class="report-product-summary__value">{{ publishTime }}</text>
<text class="report-meta__value">{{ publishTime }}</text> <text class="report-product-summary__status">{{ detail.report_header.report_status_text }}</text>
</view> </view>
</view> </view>
<view class="report-tabs"> <view class="report-tabs">
<view :class="['report-tab', activeTab === 'product' ? 'report-tab--active' : '']" @click="activeTab = 'product'">产品信息</view> <view :class="['report-tab', activeTab === 'product' ? 'report-tab--active' : '']" @click="activeTab = 'product'">产品信息</view>
<view v-if="centerTabVisible" :class="['report-tab', activeTab === 'center' ? 'report-tab--active' : '']" @click="activeTab = 'center'">鉴定中心</view>
<view :class="['report-tab', activeTab === 'attachments' ? 'report-tab--active' : '']" @click="activeTab = 'attachments'">核验附件</view> <view :class="['report-tab', activeTab === 'attachments' ? 'report-tab--active' : '']" @click="activeTab = 'attachments'">核验附件</view>
</view> </view>
@@ -340,6 +373,31 @@ onShow(() => {
<view v-if="item.remark" class="product-spec__remark">{{ item.remark }}</view> <view v-if="item.remark" class="product-spec__remark">{{ item.remark }}</view>
</view> </view>
</view> </view>
<view class="report-statement">
<view class="report-statement__title">安心验声明:</view>
<view v-for="(item, index) in anxinyanStatementItems" :key="item" class="report-statement__item">
{{ index + 1 }}{{ item }}
</view>
</view>
</view>
<view v-else-if="activeTab === 'center' && centerTabVisible" class="report-panel report-panel--center">
<view class="center-profile">
<view class="center-profile__brand">
<image class="center-profile__logo" src="/static/logo.png" mode="aspectFit" />
<view class="center-profile__text">
<view class="center-profile__name">{{ institutionBannerTitle }}</view>
<view class="center-profile__subtitle">{{ institutionBannerSubtitle }}</view>
</view>
</view>
<view class="center-profile__divider"></view>
<view class="center-profile__body">
<view v-for="item in centerIntroParagraphs" :key="item" class="center-profile__paragraph">
{{ item }}
</view>
</view>
</view>
</view> </view>
<view v-else class="report-panel report-panel--attachments"> <view v-else class="report-panel report-panel--attachments">
@@ -421,9 +479,10 @@ onShow(() => {
.report-page { .report-page {
width: 100vw; width: 100vw;
max-width: 100vw; max-width: 100vw;
padding: 28rpx 32rpx 48rpx; box-sizing: border-box;
padding: 0 0 48rpx;
overflow-x: hidden; overflow-x: hidden;
background: #f1f3f6; background: #eef1f5;
color: #3c3f45; color: #3c3f45;
} }
@@ -432,6 +491,7 @@ onShow(() => {
} }
.notice-card { .notice-card {
margin: 28rpx 32rpx;
padding: 42rpx 34rpx; padding: 42rpx 34rpx;
border-radius: 22rpx; border-radius: 22rpx;
background: #ffffff; background: #ffffff;
@@ -457,7 +517,7 @@ onShow(() => {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 22rpx; gap: 22rpx;
margin-bottom: 22rpx; margin: 22rpx 28rpx;
padding: 22rpx 24rpx; padding: 22rpx 24rpx;
border: 1px solid rgba(223, 183, 51, 0.34); border: 1px solid rgba(223, 183, 51, 0.34);
border-radius: 18rpx; border-radius: 18rpx;
@@ -493,15 +553,16 @@ onShow(() => {
.report-shell { .report-shell {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-radius: 28rpx; width: 100%;
border-radius: 0;
background: #ffffff; background: #ffffff;
box-shadow: 0 18rpx 48rpx rgba(31, 36, 48, 0.08); box-shadow: none;
} }
.report-shell__watermark { .report-shell__watermark {
position: absolute; position: absolute;
z-index: 0; z-index: 0;
top: 374rpx; top: 530rpx;
left: 28rpx; left: 28rpx;
right: 28rpx; right: 28rpx;
height: 560rpx; height: 560rpx;
@@ -511,18 +572,63 @@ onShow(() => {
} }
.report-cover, .report-cover,
.report-meta, .report-institution-banner,
.report-product-summary,
.report-tabs, .report-tabs,
.report-panel { .report-panel {
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.report-cover { .report-institution-banner {
height: 356rpx; display: flex;
margin: 28rpx 28rpx 0; align-items: center;
gap: 14rpx;
min-height: 124rpx;
padding: 0 28rpx;
border-bottom: 1px solid rgba(229, 229, 229, 0.7);
background: #ffffff;
}
.report-institution-banner__logo {
flex: 0 0 auto;
display: block;
width: 66rpx;
height: 66rpx;
}
.report-institution-banner__text {
flex: 1;
min-width: 0;
}
.report-institution-banner__title {
overflow: hidden; overflow: hidden;
border-radius: 10rpx; color: #34363b;
font-size: 21rpx;
font-weight: 900;
line-height: 1.24;
text-overflow: ellipsis;
white-space: nowrap;
}
.report-institution-banner__subtitle {
margin-top: 5rpx;
overflow: hidden;
color: #737982;
font-size: 13rpx;
font-weight: 700;
line-height: 1.25;
letter-spacing: 0;
text-overflow: ellipsis;
white-space: nowrap;
}
.report-cover {
height: 412rpx;
margin: 0;
overflow: hidden;
border-radius: 0;
background: #e8eaee; background: #e8eaee;
} }
@@ -545,78 +651,89 @@ onShow(() => {
font-size: 24rpx; font-size: 24rpx;
} }
.report-meta { .report-product-summary {
padding: 34rpx 28rpx 12rpx; display: grid;
gap: 14rpx;
padding: 20rpx 28rpx 22rpx;
background: #e7e9ef;
} }
.report-meta__row { .report-product-summary__row {
display: flex; display: flex;
align-items: flex-start; align-items: center;
gap: 18rpx; gap: 14rpx;
min-width: 0; min-width: 0;
min-height: 48rpx;
} }
.report-meta__row + .report-meta__row { .report-product-summary__tag {
margin-top: 20rpx; flex: 0 0 auto;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 36rpx;
padding: 0 14rpx;
border: 1px solid rgba(237, 189, 0, 0.78);
border-radius: 6rpx;
background: rgba(255, 255, 255, 0.62);
color: var(--work-accent);
font-size: 22rpx;
font-weight: 800;
line-height: 1;
white-space: nowrap;
} }
.report-meta__label { .report-product-summary__tag--muted {
flex: 0 0 138rpx; border-color: #b8bdc6;
display: block; color: #5d636d;
color: #7d828a;
font-size: 28rpx;
line-height: 1.4;
} }
.report-meta__value { .report-product-summary__value {
flex: 1; flex: 1;
display: block;
min-width: 0; min-width: 0;
color: #44474d; color: #2f333a;
font-size: 28rpx; font-size: 24rpx;
font-weight: 700; font-weight: 800;
line-height: 1.35; line-height: 1.45;
text-align: right;
white-space: normal;
word-break: break-all; word-break: break-all;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
.report-meta__row--date .report-meta__value { .report-product-summary__status {
flex: 0 1 auto;
margin-left: auto;
word-break: normal;
}
.report-meta__tag {
flex: 0 0 auto; flex: 0 0 auto;
display: block; display: inline-flex;
min-height: 38rpx; align-items: center;
padding: 0 16rpx; justify-content: center;
border: 1px solid rgba(221, 179, 47, 0.74); min-height: 34rpx;
padding: 0 12rpx;
border: 1px solid rgba(237, 189, 0, 0.78);
border-radius: 6rpx; border-radius: 6rpx;
background: #fff9e6; background: rgba(255, 255, 255, 0.68);
color: #9f8433; color: var(--work-accent);
font-size: 22rpx; font-size: 21rpx;
font-weight: 700; font-weight: 800;
line-height: 36rpx; line-height: 1;
white-space: nowrap;
} }
.report-tabs { .report-tabs {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 104rpx; padding: 24rpx 20rpx 34rpx;
padding: 26rpx 40rpx 34rpx; border-top: 1px solid rgba(229, 229, 229, 0.72);
border-bottom: 1px solid rgba(229, 229, 229, 0.72);
} }
.report-tab { .report-tab {
position: relative; position: relative;
flex: 1;
min-width: 0;
color: #8e9298; color: #8e9298;
font-size: 32rpx; font-size: 28rpx;
font-weight: 700; font-weight: 700;
line-height: 1.6; line-height: 1.6;
text-align: center;
white-space: nowrap;
} }
.report-tab--active { .report-tab--active {
@@ -650,7 +767,11 @@ onShow(() => {
gap: 24rpx; gap: 24rpx;
min-height: 132rpx; min-height: 132rpx;
padding: 10rpx 136rpx 30rpx 0; padding: 10rpx 136rpx 30rpx 0;
border: 0;
border-bottom: 1px solid #e5e5e5; border-bottom: 1px solid #e5e5e5;
border-radius: 0;
background: transparent;
box-shadow: none;
} }
.report-result__content { .report-result__content {
@@ -742,6 +863,99 @@ onShow(() => {
line-height: 1.45; line-height: 1.45;
} }
.report-statement {
position: relative;
z-index: 1;
margin-top: 32rpx;
padding-top: 10rpx;
color: #343840;
}
.report-statement__title {
font-size: 28rpx;
font-weight: 900;
line-height: 1.7;
}
.report-statement__item {
margin-top: 16rpx;
color: #3f444c;
font-size: 26rpx;
font-weight: 700;
line-height: 2;
word-break: break-all;
overflow-wrap: anywhere;
}
.report-panel--center {
padding-top: 28rpx;
}
.center-profile {
position: relative;
z-index: 1;
}
.center-profile__brand {
display: flex;
align-items: center;
gap: 18rpx;
min-width: 0;
}
.center-profile__logo {
flex: 0 0 auto;
display: block;
width: 72rpx;
height: 72rpx;
}
.center-profile__text {
flex: 1;
min-width: 0;
}
.center-profile__name {
color: #30333a;
font-size: 26rpx;
font-weight: 900;
line-height: 1.34;
word-break: break-all;
overflow-wrap: anywhere;
}
.center-profile__subtitle {
margin-top: 6rpx;
color: #66707c;
font-size: 18rpx;
font-weight: 700;
line-height: 1.25;
letter-spacing: 0;
word-break: break-all;
overflow-wrap: anywhere;
}
.center-profile__divider {
width: 100%;
height: 1px;
margin: 26rpx 0 24rpx;
background: #e5e5e5;
}
.center-profile__body {
display: grid;
gap: 18rpx;
}
.center-profile__paragraph {
color: #767b84;
font-size: 26rpx;
line-height: 1.82;
text-align: justify;
word-break: break-all;
overflow-wrap: anywhere;
}
.report-panel--attachments { .report-panel--attachments {
padding-top: 2rpx; padding-top: 2rpx;
} }
@@ -840,9 +1054,9 @@ onShow(() => {
bottom: 0; bottom: 0;
width: 100vw; width: 100vw;
box-sizing: border-box; box-sizing: border-box;
padding: 22rpx 32rpx calc(22rpx + env(safe-area-inset-bottom)); padding: 0 0 env(safe-area-inset-bottom);
background: rgba(241, 243, 246, 0.96); background: #ffffff;
box-shadow: 0 -10rpx 34rpx rgba(31, 36, 48, 0.05); box-shadow: none;
} }
.report-actions__button { .report-actions__button {
@@ -850,13 +1064,18 @@ onShow(() => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
min-height: 82rpx; min-height: 112rpx;
border-radius: 999rpx; border-radius: 0;
background: #dfb733; background: #edbd00;
color: #ffffff; color: #ffffff;
font-size: 28rpx; font-size: 32rpx;
font-weight: 800; font-weight: 800;
line-height: 82rpx; line-height: 112rpx;
box-shadow: none;
}
.report-actions__button::after {
border: 0;
} }
.video-preview-mask { .video-preview-mask {