|
|
@@ -112,82 +112,42 @@
|
|
|
|
|
|
<!-- Right Column: Logistics Info -->
|
|
|
<div class="info-column">
|
|
|
- <div class="info-item">
|
|
|
- <span class="label">发货物流:</span>
|
|
|
- <span class="value">{{
|
|
|
- detail.expressName || '-'
|
|
|
- }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <span class="label">发货单号:</span>
|
|
|
- <span class="value">{{
|
|
|
- detail.waybillCode || '-'
|
|
|
- }}</span>
|
|
|
-
|
|
|
- <el-popover
|
|
|
- placement="bottom"
|
|
|
- title="物流动态"
|
|
|
- :width="400"
|
|
|
- trigger="hover"
|
|
|
- >
|
|
|
- <template #reference>
|
|
|
- <el-button
|
|
|
- link
|
|
|
- type="success"
|
|
|
- class="ml-2"
|
|
|
- v-if="detail.waybillCode"
|
|
|
- >查看</el-button
|
|
|
- >
|
|
|
- </template>
|
|
|
- <el-timeline
|
|
|
- style="max-height: 400px; overflow-y: auto"
|
|
|
- v-if="
|
|
|
- detail.trackingVoList &&
|
|
|
- detail.trackingVoList.length > 0
|
|
|
- "
|
|
|
- >
|
|
|
- <el-timeline-item
|
|
|
- v-for="(
|
|
|
- activity, index
|
|
|
- ) in detail.trackingVoList"
|
|
|
- :key="index"
|
|
|
- :timestamp="activity.ftime"
|
|
|
- :type="index === 0 ? 'success' : ''"
|
|
|
- :color="index === 0 ? '#0bbd87' : ''"
|
|
|
- >
|
|
|
- <div>
|
|
|
- <div
|
|
|
- style="margin-bottom: 5px"
|
|
|
- v-if="activity.status"
|
|
|
- >
|
|
|
- <el-tag
|
|
|
- size="small"
|
|
|
- :type="
|
|
|
- index === 0
|
|
|
- ? 'success'
|
|
|
- : 'info'
|
|
|
- "
|
|
|
- >{{ activity.status }}</el-tag
|
|
|
- >
|
|
|
- </div>
|
|
|
- <div
|
|
|
- style="
|
|
|
- color: #606266;
|
|
|
- font-size: 13px;
|
|
|
- line-height: 1.5;
|
|
|
- "
|
|
|
- >{{ activity.context }}</div
|
|
|
- >
|
|
|
- </div>
|
|
|
- </el-timeline-item>
|
|
|
- </el-timeline>
|
|
|
- <el-empty
|
|
|
- v-else
|
|
|
- description="暂无物流信息"
|
|
|
- :image-size="60"
|
|
|
- />
|
|
|
- </el-popover>
|
|
|
- </div>
|
|
|
+ <template v-if="expressPackages.length">
|
|
|
+ <div v-for="(pkg, pkgIndex) in expressPackages" :key="pkgIndex" class="package-block">
|
|
|
+ <div class="info-item" v-if="expressPackages.length > 1">
|
|
|
+ <span class="label"></span>
|
|
|
+ <span class="package-tag">包裹{{ pkgIndex + 1 }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">发货物流:</span>
|
|
|
+ <span class="value">{{ pkg.expressName || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">发货单号:</span>
|
|
|
+ <span class="value">{{ pkg.waybillCode || '-' }}</span>
|
|
|
+ <el-popover v-if="pkg.waybillCode || pkg.tracks.length" placement="bottom" :title="popoverTitle(pkg)" :width="400" trigger="hover">
|
|
|
+ <template #reference>
|
|
|
+ <el-button link type="success" class="ml-2">查看</el-button>
|
|
|
+ </template>
|
|
|
+ <el-timeline v-if="pkg.tracks.length" class="track-timeline">
|
|
|
+ <el-timeline-item v-for="(activity, index) in pkg.tracks" :key="index" :timestamp="activity.ftime" :type="index === 0 ? 'success' : ''" :color="index === 0 ? '#0bbd87' : ''">
|
|
|
+ <div>
|
|
|
+ <div style="margin-bottom: 5px" v-if="activity.status">
|
|
|
+ <el-tag size="small" :type="index === 0 ? 'success' : 'info'">{{ activity.status }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <div style="color: #606266; font-size: 13px; line-height: 1.5">{{ activity.context }}</div>
|
|
|
+ </div>
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
+ <el-empty v-else description="暂无物流信息" :image-size="60" />
|
|
|
+ </el-popover>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div class="info-item"><span class="label">发货物流:</span><span class="value">-</span></div>
|
|
|
+ <div class="info-item"><span class="label">发货单号:</span><span class="value">-</span></div>
|
|
|
+ </template>
|
|
|
|
|
|
<div class="action-area mt-2" v-if="detail.status >= 3">
|
|
|
<el-button type="success" @click="$emit('add-package')"
|
|
|
@@ -220,8 +180,7 @@
|
|
|
|
|
|
<script setup>
|
|
|
import { Lightning } from '@element-plus/icons-vue';
|
|
|
- import { EleMessage } from 'ele-admin-plus/es';
|
|
|
- import { ref } from 'vue';
|
|
|
+ import { computed } from 'vue';
|
|
|
import wxIcon from '@/assets/wx.png';
|
|
|
import alipayIcon from '@/assets/alipay.png';
|
|
|
import { formatPhone } from '@/utils/common';
|
|
|
@@ -236,6 +195,50 @@
|
|
|
|
|
|
defineEmits(['add-package', 'edit-receiver']);
|
|
|
|
|
|
+ const isTrackNode = (item) => {
|
|
|
+ if (!item || typeof item !== 'object') return false;
|
|
|
+ const hasTrack = item.context || item.ftime || item.status;
|
|
|
+ const hasPackage = item.waybillCode || item.expressName || Array.isArray(item.trackingVoList);
|
|
|
+ return Boolean(hasTrack) && !hasPackage;
|
|
|
+ };
|
|
|
+ const isPackageNode = (item) => {
|
|
|
+ if (!item || typeof item !== 'object') return false;
|
|
|
+ return Boolean(item.waybillCode || item.expressName || Array.isArray(item.trackingVoList));
|
|
|
+ };
|
|
|
+ const expressPackages = computed(() => {
|
|
|
+ const detail = props.detail || {};
|
|
|
+ const list = Array.isArray(detail.trackingVoList) ? detail.trackingVoList : [];
|
|
|
+ const packages = list.filter(isPackageNode).map((item) => ({
|
|
|
+ expressName: item.expressName || '',
|
|
|
+ waybillCode: item.waybillCode || '',
|
|
|
+ tracks: Array.isArray(item.trackingVoList) ? item.trackingVoList : []
|
|
|
+ }));
|
|
|
+ const flatTracks = list.filter(isTrackNode);
|
|
|
+ const legacy = {
|
|
|
+ expressName: detail.expressName || '',
|
|
|
+ waybillCode: detail.waybillCode || '',
|
|
|
+ tracks: flatTracks
|
|
|
+ };
|
|
|
+ const hasLegacy = legacy.expressName || legacy.waybillCode || flatTracks.length;
|
|
|
+ const duplicated = packages.some((pkg) => {
|
|
|
+ if (legacy.waybillCode && pkg.waybillCode) return pkg.waybillCode === legacy.waybillCode;
|
|
|
+ return legacy.expressName && pkg.expressName === legacy.expressName && !legacy.waybillCode && !pkg.waybillCode;
|
|
|
+ });
|
|
|
+ if (hasLegacy && duplicated) {
|
|
|
+ const hit = packages.find((pkg) => legacy.waybillCode && pkg.waybillCode === legacy.waybillCode) || packages[0];
|
|
|
+ if (hit && !hit.tracks.length && flatTracks.length) hit.tracks = flatTracks;
|
|
|
+ } else if (hasLegacy) {
|
|
|
+ packages.unshift(legacy);
|
|
|
+ }
|
|
|
+ return packages.filter((pkg) => pkg.expressName || pkg.waybillCode || pkg.tracks.length);
|
|
|
+ });
|
|
|
+ const popoverTitle = (pkg) => {
|
|
|
+ if (expressPackages.value.length > 1) {
|
|
|
+ return ((pkg.expressName || '物流') + ' ' + (pkg.waybillCode || '')).trim();
|
|
|
+ }
|
|
|
+ return '物流动态';
|
|
|
+ };
|
|
|
+
|
|
|
const getOutOfStockText = (value) => {
|
|
|
const map = {
|
|
|
1: '缺货时电话与我沟通',
|
|
|
@@ -283,4 +286,23 @@
|
|
|
.action-area {
|
|
|
margin-top: auto;
|
|
|
}
|
|
|
+
|
|
|
+ .popover-package + .popover-package {
|
|
|
+ margin-top: 12px;
|
|
|
+ padding-top: 12px;
|
|
|
+ border-top: 1px solid #ebeef5;
|
|
|
+ }
|
|
|
+
|
|
|
+ .popover-package-title {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ color: #303133;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ .package-tag { color: #67c23a; font-size: 12px; }
|
|
|
+ .package-block + .package-block { margin-top: 4px; }
|
|
|
+ .track-timeline {
|
|
|
+ max-height: 400px;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
</style>
|