fix: simplify work app return report review
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { adminApi, type AdminReportDetail } from "../../api/admin";
|
||||
import { adminApi, type AdminFileAsset, type AdminReportDetail } from "../../api/admin";
|
||||
import { showErrorToast, showInfoToast } from "../../utils/feedback";
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -11,37 +11,122 @@ const detail = ref<AdminReportDetail | null>(null);
|
||||
const reportId = ref(0);
|
||||
const returnInternalTagNo = ref("");
|
||||
const returnConfirming = ref(false);
|
||||
const activeVideo = ref<AdminFileAsset | null>(null);
|
||||
|
||||
const isZhongjian = computed(() => detail.value?.report_header.service_provider === "zhongjian");
|
||||
const isReturnReview = computed(() => Boolean(returnInternalTagNo.value && reportId.value));
|
||||
const reportMetaItems = computed(() => {
|
||||
const current = detail.value;
|
||||
if (!current) return [];
|
||||
|
||||
const items = [
|
||||
{ label: "发布时间", value: current.report_header.publish_time || "-" },
|
||||
];
|
||||
const appraiserName = textValue(current.appraisal_info?.appraiser_name)
|
||||
|| textValue(current.report_header.report_entry_admin_name);
|
||||
if (appraiserName) {
|
||||
items.push({ label: "鉴定师", value: appraiserName });
|
||||
}
|
||||
if (isZhongjian.value) {
|
||||
items.push({ label: "中检报告号", value: current.report_header.zhongjian_report_no || "-" });
|
||||
items.push({ label: "报告录入人", value: current.report_header.report_entry_admin_name || "-" });
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
const resultMetaItems = computed(() => {
|
||||
const current = detail.value;
|
||||
if (!current) return [];
|
||||
|
||||
const result = current.result_info || {};
|
||||
const valuation = current.valuation_info || {};
|
||||
const items = [
|
||||
{ label: "结论", value: textValue(result.result_text) || "-" },
|
||||
{ label: "结论说明", value: textValue(result.result_desc) || "-" },
|
||||
];
|
||||
|
||||
for (const point of normalizedKeyPoints(result.key_points)) {
|
||||
items.push({
|
||||
label: point.point_name,
|
||||
value: [point.point_value, point.point_remark].filter(Boolean).join(";") || "-",
|
||||
});
|
||||
}
|
||||
|
||||
const conditionGrade = textValue(valuation.condition_grade);
|
||||
const conditionDesc = textValue(valuation.condition_desc);
|
||||
if (conditionGrade || conditionDesc) {
|
||||
items.push({ label: "成色评级", value: [conditionGrade, conditionDesc].filter(Boolean).join(";") || "-" });
|
||||
}
|
||||
|
||||
const valuationMin = Number(valuation.valuation_min || 0);
|
||||
const valuationMax = Number(valuation.valuation_max || 0);
|
||||
const valuationDesc = textValue(valuation.valuation_desc);
|
||||
if (valuationMin > 0 || valuationMax > 0 || valuationDesc) {
|
||||
const range = valuationMin > 0 || valuationMax > 0 ? `¥${valuationMin} - ¥${valuationMax}` : "";
|
||||
items.push({ label: "估值", value: [range, valuationDesc].filter(Boolean).join(";") || "-" });
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
const evidenceAttachments = computed(() => detail.value?.evidence_attachments || []);
|
||||
const zhongjianReportFiles = computed(() => isZhongjian.value ? (detail.value?.zhongjian_report_files || []) : []);
|
||||
|
||||
function previewImage(urls: string[], current: string) {
|
||||
if (!urls.length) return;
|
||||
uni.previewImage({ urls, current });
|
||||
}
|
||||
|
||||
function openAsset(item: { file_url: string; file_type?: string; thumbnail_url?: string }) {
|
||||
if (item.file_type === "image") {
|
||||
function textValue(value: unknown) {
|
||||
return String(value ?? "").trim();
|
||||
}
|
||||
|
||||
function normalizedKeyPoints(value: unknown) {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value
|
||||
.map((item) => {
|
||||
const point = item as Record<string, unknown>;
|
||||
return {
|
||||
point_code: textValue(point.point_code),
|
||||
point_name: textValue(point.point_name) || "鉴定项",
|
||||
point_value: textValue(point.point_value),
|
||||
point_remark: textValue(point.point_remark),
|
||||
};
|
||||
})
|
||||
.filter((item) => item.point_value || item.point_remark);
|
||||
}
|
||||
|
||||
function isImageAsset(item: AdminFileAsset) {
|
||||
return item.file_type === "image" || item.mime_type?.startsWith("image/");
|
||||
}
|
||||
|
||||
function isVideoAsset(item: AdminFileAsset) {
|
||||
return item.file_type === "video" || item.mime_type?.startsWith("video/");
|
||||
}
|
||||
|
||||
function assetTypeLabel(item: AdminFileAsset) {
|
||||
if (isImageAsset(item)) return "图片";
|
||||
if (isVideoAsset(item)) return "视频";
|
||||
if (item.file_type === "pdf" || item.mime_type === "application/pdf") return "PDF";
|
||||
return "附件";
|
||||
}
|
||||
|
||||
function openAsset(item: AdminFileAsset) {
|
||||
if (isImageAsset(item)) {
|
||||
const urls = [
|
||||
...(detail.value?.evidence_attachments || []).map((asset) => asset.file_url),
|
||||
...(detail.value?.zhongjian_report_files || []).map((asset) => asset.file_url),
|
||||
...evidenceAttachments.value.filter(isImageAsset).map((asset) => asset.file_url),
|
||||
...zhongjianReportFiles.value.filter(isImageAsset).map((asset) => asset.file_url),
|
||||
];
|
||||
previewImage(urls, item.file_url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.file_type === "video") {
|
||||
const previewMedia = (uni as any).previewMedia;
|
||||
if (typeof previewMedia === "function") {
|
||||
previewMedia({
|
||||
sources: [{ url: item.file_url, type: "video", poster: item.thumbnail_url || "" }],
|
||||
current: 0,
|
||||
});
|
||||
if (isVideoAsset(item)) {
|
||||
activeVideo.value = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.file_type === "pdf") {
|
||||
if (item.file_type === "pdf" || item.mime_type === "application/pdf") {
|
||||
uni.showLoading({ title: "准备打开" });
|
||||
uni.downloadFile({
|
||||
url: item.file_url,
|
||||
success: (response) => {
|
||||
@@ -56,6 +141,7 @@ function openAsset(item: { file_url: string; file_type?: string; thumbnail_url?:
|
||||
});
|
||||
},
|
||||
fail: () => uni.showToast({ title: "附件打开失败", icon: "none" }),
|
||||
complete: () => uni.hideLoading(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -63,6 +149,10 @@ function openAsset(item: { file_url: string; file_type?: string; thumbnail_url?:
|
||||
uni.showToast({ title: "当前附件暂不支持打开", icon: "none" });
|
||||
}
|
||||
|
||||
function closeVideo() {
|
||||
activeVideo.value = null;
|
||||
}
|
||||
|
||||
async function fetchDetail() {
|
||||
if (!reportId.value) return;
|
||||
loading.value = true;
|
||||
@@ -145,7 +235,7 @@ onShow(() => {
|
||||
|
||||
<view v-if="isReturnReview" class="card return-review-card">
|
||||
<view class="card-title">回寄前报告核对</view>
|
||||
<view class="card-desc">请核对报告编号、结论、附件和验真信息,确认无误后进入回寄信息填写。</view>
|
||||
<view class="card-desc">请核对报告编号、结论和附件,确认无误后进入回寄信息填写。</view>
|
||||
<button class="btn btn--primary main-action" :disabled="returnConfirming" @click="confirmReturnFromReport">
|
||||
{{ returnConfirming ? "确认中" : "确认寄回" }}
|
||||
</button>
|
||||
@@ -160,21 +250,9 @@ onShow(() => {
|
||||
<text class="tag">{{ detail.report_header.service_provider_text }}</text>
|
||||
</view>
|
||||
<view class="meta-grid">
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">发布时间</view>
|
||||
<view class="meta-value">{{ detail.report_header.publish_time || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">录入人</view>
|
||||
<view class="meta-value">{{ detail.report_header.report_entry_admin_name || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">中检报告号</view>
|
||||
<view class="meta-value">{{ detail.report_header.zhongjian_report_no || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">验真次数</view>
|
||||
<view class="meta-value">{{ detail.verify_info.verify_count }}</view>
|
||||
<view v-for="item in reportMetaItems" :key="item.label" class="meta-item">
|
||||
<view class="meta-label">{{ item.label }}</view>
|
||||
<view class="meta-value">{{ item.value }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -200,79 +278,63 @@ onShow(() => {
|
||||
<view class="card">
|
||||
<view class="card-title">鉴定结果</view>
|
||||
<view class="meta-grid">
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">结论</view>
|
||||
<view class="meta-value">{{ detail.result_info.result_text || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">结论说明</view>
|
||||
<view class="meta-value">{{ detail.result_info.result_desc || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">评级</view>
|
||||
<view class="meta-value">{{ detail.valuation_info.condition_grade || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">估值区间</view>
|
||||
<view class="meta-value">¥{{ detail.valuation_info.valuation_min || 0 }} - ¥{{ detail.valuation_info.valuation_max || 0 }}</view>
|
||||
<view v-for="(item, index) in resultMetaItems" :key="`${item.label}-${index}`" class="meta-item">
|
||||
<view class="meta-label">{{ item.label }}</view>
|
||||
<view class="meta-value">{{ item.value }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">附件</view>
|
||||
<view v-if="detail.evidence_attachments.length" class="list" style="margin-top: 18rpx">
|
||||
<view
|
||||
v-for="item in detail.evidence_attachments"
|
||||
:key="item.file_id"
|
||||
class="list-card"
|
||||
@click="openAsset(item)"
|
||||
>
|
||||
<view class="row">
|
||||
<view class="list-title">{{ item.name || item.file_id }}</view>
|
||||
<text class="tag">{{ item.file_type || "附件" }}</text>
|
||||
<view v-if="evidenceAttachments.length" class="attachment-grid">
|
||||
<view v-for="item in evidenceAttachments" :key="item.file_url || item.file_id" class="attachment-tile">
|
||||
<view class="attachment-preview" @click="openAsset(item)">
|
||||
<image v-if="isImageAsset(item)" class="attachment-thumb" :src="item.thumbnail_url || item.file_url" mode="aspectFill" />
|
||||
<template v-else-if="isVideoAsset(item)">
|
||||
<image v-if="item.thumbnail_url" class="attachment-thumb" :src="item.thumbnail_url" mode="aspectFill" />
|
||||
<view v-else class="attachment-video-thumb">
|
||||
<text class="attachment-video-label">视频</text>
|
||||
</view>
|
||||
</template>
|
||||
<view v-else class="attachment-file-thumb">{{ assetTypeLabel(item) }}</view>
|
||||
<view v-if="isVideoAsset(item)" class="attachment-play" @click.stop="openAsset(item)">▶</view>
|
||||
</view>
|
||||
<view class="attachment-name">{{ item.name || item.file_id }}</view>
|
||||
<text class="attachment-type">{{ assetTypeLabel(item) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty" style="padding: 24rpx 0">暂无证据附件</view>
|
||||
</view>
|
||||
|
||||
<view v-if="detail.zhongjian_report_files.length" class="card">
|
||||
<view v-if="zhongjianReportFiles.length" class="card">
|
||||
<view class="card-title">中检报告文件</view>
|
||||
<view class="list" style="margin-top: 18rpx">
|
||||
<view
|
||||
v-for="item in detail.zhongjian_report_files"
|
||||
:key="item.file_id"
|
||||
class="list-card"
|
||||
@click="openAsset(item)"
|
||||
>
|
||||
<view class="row">
|
||||
<view class="list-title">{{ item.name || item.file_id }}</view>
|
||||
<text class="tag">{{ item.file_type || "附件" }}</text>
|
||||
<view class="attachment-grid">
|
||||
<view v-for="item in zhongjianReportFiles" :key="item.file_url || item.file_id" class="attachment-tile">
|
||||
<view class="attachment-preview" @click="openAsset(item)">
|
||||
<image v-if="isImageAsset(item)" class="attachment-thumb" :src="item.thumbnail_url || item.file_url" mode="aspectFill" />
|
||||
<template v-else-if="isVideoAsset(item)">
|
||||
<image v-if="item.thumbnail_url" class="attachment-thumb" :src="item.thumbnail_url" mode="aspectFill" />
|
||||
<view v-else class="attachment-video-thumb">
|
||||
<text class="attachment-video-label">视频</text>
|
||||
</view>
|
||||
</template>
|
||||
<view v-else class="attachment-file-thumb">{{ assetTypeLabel(item) }}</view>
|
||||
<view v-if="isVideoAsset(item)" class="attachment-play" @click.stop="openAsset(item)">▶</view>
|
||||
</view>
|
||||
<view class="attachment-name">{{ item.name || item.file_id }}</view>
|
||||
<text class="attachment-type">{{ assetTypeLabel(item) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="card-title">验真信息</view>
|
||||
<view class="meta-grid">
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">验真状态</view>
|
||||
<view class="meta-value">{{ isZhongjian ? "中检报告" : detail.verify_info.verify_status || "valid" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">验真页</view>
|
||||
<view class="meta-value">{{ detail.verify_info.verify_url || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">验真二维码</view>
|
||||
<view class="meta-value">{{ detail.verify_info.verify_qrcode_url || "-" }}</view>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">报告页</view>
|
||||
<view class="meta-value">{{ detail.verify_info.report_page_url || "-" }}</view>
|
||||
<view v-if="activeVideo" class="video-preview-mask" @click="closeVideo">
|
||||
<view class="video-preview-panel" @click.stop>
|
||||
<view class="video-preview-head">
|
||||
<text class="video-preview-title">{{ activeVideo.name || "附件视频" }}</text>
|
||||
<text class="video-preview-close" @click="closeVideo">关闭</text>
|
||||
</view>
|
||||
<video class="video-preview-player" :src="activeVideo.file_url" :poster="activeVideo.thumbnail_url || ''" controls autoplay />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -292,4 +354,148 @@ onShow(() => {
|
||||
.main-action {
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.attachment-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 14rpx;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.attachment-tile {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--work-border);
|
||||
border-radius: var(--work-radius-sm);
|
||||
background: var(--work-card-muted);
|
||||
}
|
||||
|
||||
.attachment-thumb {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-video-thumb,
|
||||
.attachment-file-thumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-video-thumb {
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e8edf3 100%);
|
||||
color: var(--work-text);
|
||||
}
|
||||
|
||||
.attachment-video-label {
|
||||
font-size: 24rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.attachment-file-thumb {
|
||||
color: var(--work-text-soft);
|
||||
font-size: 24rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.attachment-play {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 54rpx;
|
||||
height: 54rpx;
|
||||
margin-left: -27rpx;
|
||||
margin-top: -27rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(32, 33, 36, 0.72);
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
line-height: 54rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
min-width: 0;
|
||||
margin-top: 8rpx;
|
||||
overflow: hidden;
|
||||
color: var(--work-text);
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.attachment-type {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 34rpx;
|
||||
margin-top: 6rpx;
|
||||
padding: 0 10rpx;
|
||||
border-radius: var(--work-radius-pill);
|
||||
background: var(--work-info-soft);
|
||||
color: var(--work-info);
|
||||
font-size: 20rpx;
|
||||
font-weight: 700;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
|
||||
.video-preview-mask {
|
||||
position: fixed;
|
||||
z-index: 20;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx;
|
||||
background: rgba(0, 0, 0, 0.58);
|
||||
}
|
||||
|
||||
.video-preview-panel {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-radius: var(--work-radius);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.video-preview-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18rpx;
|
||||
padding: 22rpx 24rpx;
|
||||
}
|
||||
|
||||
.video-preview-title {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: var(--work-text);
|
||||
font-size: 28rpx;
|
||||
font-weight: 800;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.video-preview-close {
|
||||
flex: 0 0 auto;
|
||||
color: var(--work-info);
|
||||
font-size: 26rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.video-preview-player {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 58vh;
|
||||
background: #000000;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user