完善各种需求

This commit is contained in:
BBIT-Kai
2026-04-14 16:52:30 +08:00
parent 1c68762421
commit 44181bcf5a
19 changed files with 1848 additions and 282 deletions
@@ -19,7 +19,13 @@ import {
import { getTraceabilityBatches, getTraceabilityPreviewDetail } from '#/api';
import { formatFieldValue, getFieldDisplayStyle, getImagePreviewSrc } from './shared';
import {
buildCoordinateEmbedUrl,
buildCoordinateMapUrl,
formatFieldValue,
getFieldDisplayStyle,
getImagePreviewSrc,
} from './shared';
const loading = ref(false);
const batches = ref<TraceabilityApi.BatchSummary[]>([]);
@@ -32,6 +38,23 @@ const qrCode = useQRCode(publicLink, {
margin: 2,
width: 220,
});
const qrDownloadName = computed(
() => `${detail.value?.batch.batchCode || 'batch'}.png`,
);
function downloadQrCode(dataUrl: string, fileName: string) {
if (!dataUrl) return;
const link = document.createElement('a');
link.href = dataUrl;
link.download = fileName;
document.body.append(link);
link.click();
link.remove();
}
function downloadConsumerQrCode() {
downloadQrCode(qrCode.value, qrDownloadName.value);
}
async function loadBatches() {
batches.value = await getTraceabilityBatches();
@@ -136,6 +159,16 @@ onMounted(loadBatches);
<Row :gutter="[16, 16]" class="feature-row">
<Col :lg="8" :xs="24">
<Card class="panel-card qr-panel" title="二维码">
<template #extra>
<Button
v-if="publicLink"
size="small"
type="primary"
@click="downloadConsumerQrCode"
>
保存二维码
</Button>
</template>
<div class="qr-wrap">
<img :src="qrCode" alt="溯源二维码" class="qr-image" />
<div class="qr-meta">
@@ -210,10 +243,38 @@ onMounted(loadBatches);
<span>{{ entry.label }}</span>
<img
v-if="entry.type === 'image' && entry.value"
:src="getImagePreviewSrc(entry.value, item.valuePreviewUrls?.[entry.key])"
:src="
getImagePreviewSrc(
entry.value,
item.valuePreviewUrls?.[entry.key],
)
"
:alt="entry.label"
class="consumer-image"
/>
<div
v-else-if="entry.type === 'coordinate' && entry.value"
class="coordinate-preview-card"
>
<iframe
v-if="buildCoordinateEmbedUrl(entry.value)"
:src="buildCoordinateEmbedUrl(entry.value)"
class="coordinate-preview-map"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
:title="entry.label"
></iframe>
<strong :style="getFieldDisplayStyle(entry.field)">
{{ formatFieldValue(entry.value) }}
</strong>
<a
v-if="buildCoordinateMapUrl(entry.value)"
:href="buildCoordinateMapUrl(entry.value)"
target="_blank"
rel="noreferrer"
>打开地图</a
>
</div>
<strong v-else :style="getFieldDisplayStyle(entry.field)">
{{ formatFieldValue(entry.value) }}
</strong>
@@ -256,10 +317,38 @@ onMounted(loadBatches);
<span>{{ entry.label }}</span>
<img
v-if="entry.type === 'image' && entry.value"
:src="getImagePreviewSrc(entry.value, item.valuePreviewUrls?.[entry.key])"
:src="
getImagePreviewSrc(
entry.value,
item.valuePreviewUrls?.[entry.key],
)
"
:alt="entry.label"
class="consumer-image"
/>
<div
v-else-if="entry.type === 'coordinate' && entry.value"
class="coordinate-preview-card"
>
<iframe
v-if="buildCoordinateEmbedUrl(entry.value)"
:src="buildCoordinateEmbedUrl(entry.value)"
class="coordinate-preview-map"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
:title="entry.label"
></iframe>
<strong :style="getFieldDisplayStyle(entry.field)">
{{ formatFieldValue(entry.value) }}
</strong>
<a
v-if="buildCoordinateMapUrl(entry.value)"
:href="buildCoordinateMapUrl(entry.value)"
target="_blank"
rel="noreferrer"
>打开地图</a
>
</div>
<strong
v-else
:style="getFieldDisplayStyle(entry.field)"
@@ -521,6 +610,24 @@ onMounted(loadBatches);
background: #fff;
}
.coordinate-preview-card {
display: grid;
gap: 10px;
}
.coordinate-preview-map {
width: 100%;
min-height: 180px;
border: 1px solid #e2e8f0;
border-radius: 14px;
background: #fff;
}
.coordinate-preview-card a {
color: #1d4ed8;
font-size: 12px;
}
.timeline {
display: grid;
gap: 16px;