|
|
@@ -22,6 +22,40 @@ watch(
|
|
|
}
|
|
|
)
|
|
|
|
|
|
+const isShowDeliveryInfo = ref(false)
|
|
|
+const deliveredInfoRef = ref()
|
|
|
+const simplexContentRef = ref()
|
|
|
+const handleSeeAllDeliveryInfo = () => {
|
|
|
+ nextTick(async () => {
|
|
|
+ const container = simplexContentRef.value
|
|
|
+ const elements = deliveredInfoRef.value
|
|
|
+
|
|
|
+ if (!container || !elements) return
|
|
|
+
|
|
|
+ const targetElement = Array.isArray(elements) ? elements[0] : elements
|
|
|
+ if (!targetElement) return
|
|
|
+
|
|
|
+ // 📏 获取布局信息
|
|
|
+ const containerRect = container.getBoundingClientRect()
|
|
|
+ const elementRect = targetElement.getBoundingClientRect()
|
|
|
+
|
|
|
+ const relativeTop = elementRect.top - containerRect.top
|
|
|
+ const scrollTop = container.scrollTop + relativeTop - 30
|
|
|
+
|
|
|
+ const maxScrollTop = container.scrollHeight - container.clientHeight
|
|
|
+ const safeScrollTop = Math.max(0, Math.min(scrollTop, maxScrollTop))
|
|
|
+
|
|
|
+ try {
|
|
|
+ container.scrollTo({
|
|
|
+ top: safeScrollTop,
|
|
|
+ behavior: 'smooth'
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ // 💠 兼容性兜底:某些浏览器不支持 'smooth' 滚动
|
|
|
+ container.scrollTop = safeScrollTop
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
// 中间点 每两个节点之间加上26px 上边距离28px 下边距离54px 如果没有中间点则高度为56px
|
|
|
const getSimplexLineHeight = (index: number) => {
|
|
|
if (index === 0) {
|
|
|
@@ -30,17 +64,34 @@ const getSimplexLineHeight = (index: number) => {
|
|
|
return 28 + 56 + 26 * (index - 1)
|
|
|
}
|
|
|
}
|
|
|
+const getDeliverySimplexHeight = (stepItem) => {
|
|
|
+ let curHeight = 0
|
|
|
+ curHeight = 28 + 26 * (stepItem.children?.length > 0 ? stepItem.children.length - 1 : 0)
|
|
|
+ // if (!stepItem.deliveredData?.length) return curHeight
|
|
|
+ // curHeight =
|
|
|
+ // curHeight + 90 * stepItem.deliveredData?.length + 8 * (stepItem.deliveredData?.length - 1)
|
|
|
+ return curHeight
|
|
|
+}
|
|
|
+const getDeliverySimplexLineHeight = (stepItem) => {
|
|
|
+ if (!stepItem.children?.length) return 0
|
|
|
+ return 28 + 26 * (stepItem.children.length - 1)
|
|
|
+}
|
|
|
+
|
|
|
+const getDeliveryInfoTop = (stepItem) => {
|
|
|
+ if (!stepItem.children?.length) return 0
|
|
|
+ return 30 + 26 * stepItem.children.length
|
|
|
+}
|
|
|
+
|
|
|
const getDateHeight = (index: number) => {
|
|
|
return 42 + 26 * index
|
|
|
}
|
|
|
-
|
|
|
const pathRef = ref()
|
|
|
|
|
|
const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <div class="simplex-content">
|
|
|
+ <div class="simplex-content" ref="simplexContentRef">
|
|
|
<div
|
|
|
class="detail-step-item"
|
|
|
:class="{
|
|
|
@@ -73,6 +124,49 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
|
|
|
<div class="label">{{ dateItem.label }}</div>
|
|
|
<div class="divider"></div>
|
|
|
<div class="date">{{ formatTimezone(dateItem.date) }}</div>
|
|
|
+ <!-- <el-button
|
|
|
+ @click="isShowDeliveryInfo = !isShowDeliveryInfo"
|
|
|
+ v-if="dateItem.label === 'Delivered'"
|
|
|
+ class="see-all-icon el-button--text"
|
|
|
+ >
|
|
|
+ See All
|
|
|
+ <span class="font_family icon-icon_next_b"></span>
|
|
|
+ </el-button> -->
|
|
|
+ <see-all-icon
|
|
|
+ v-if="dateItem.label === 'Delivered'"
|
|
|
+ v-model="isShowDeliveryInfo"
|
|
|
+ @collapse="handleSeeAllDeliveryInfo"
|
|
|
+ ></see-all-icon>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ :style="{ top: getDeliveryInfoTop(stepItem) + 'px' }"
|
|
|
+ class="delivered-info"
|
|
|
+ style="scroll-margin-top: 20px"
|
|
|
+ v-if="stepItem.label === 'Place of Delivery' && isShowDeliveryInfo"
|
|
|
+ ref="deliveredInfoRef"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="delivered-item"
|
|
|
+ v-for="(deliveredItem, deliveredIndex) in stepItem.deliveredData"
|
|
|
+ :key="deliveredIndex"
|
|
|
+ >
|
|
|
+ <div class="top">
|
|
|
+ <div class="top-left">
|
|
|
+ <div class="label">{{ deliveredItem.label }}</div>
|
|
|
+ <div class="date">
|
|
|
+ {{ formatTimezone(deliveredItem.date, deliveredItem.timezone) }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="top-right">{{ deliveredItem.location }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="bottom">
|
|
|
+ <span class="font_family icon-icon_container_b"></span>
|
|
|
+ <div style="margin-top: 1px; margin-left: 4px">Container:</div>
|
|
|
+ <div style="margin-left: 4px; margin-top: 3px; font-weight: 700">
|
|
|
+ {{ deliveredItem.container }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -81,6 +175,22 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
|
|
|
v-if="stepItem.label !== 'Place of Delivery'"
|
|
|
:style="{ height: getSimplexLineHeight(stepItem.children?.length || 0) + 'px' }"
|
|
|
></div>
|
|
|
+ <div
|
|
|
+ class="place-of-delivery-line"
|
|
|
+ v-if="stepItem.label === 'Place of Delivery'"
|
|
|
+ :style="{ height: getDeliverySimplexHeight(stepItem) + 'px' }"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="dashed-line"
|
|
|
+ :style="{
|
|
|
+ height: getDeliverySimplexLineHeight(stepItem) + 'px',
|
|
|
+ borderStyle: stepItem.isArrival ? 'solid' : 'dashed',
|
|
|
+ borderColor: stepItem.isArrival
|
|
|
+ ? 'var(--color-neutral-2)'
|
|
|
+ : 'var(--color-shipment-status-label-bg)'
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -88,8 +198,11 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
|
|
|
<style lang="scss" scoped>
|
|
|
// 单式样式
|
|
|
.simplex-content {
|
|
|
+ position: relative;
|
|
|
width: 100%;
|
|
|
- padding: 24px 8px 9px 16px;
|
|
|
+ height: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ padding: 24px 8px 24px 16px;
|
|
|
}
|
|
|
.detail-step-item {
|
|
|
& > .data {
|
|
|
@@ -164,6 +277,7 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
|
|
|
gap: 8px;
|
|
|
width: calc(100% + 20px);
|
|
|
.label {
|
|
|
+ margin-left: 3px;
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
.divider {
|
|
|
@@ -175,14 +289,79 @@ const { isOverflow: isPathOverflow } = useOverflow(pathRef, props)
|
|
|
.date {
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
+ :deep(.see-all-icon) {
|
|
|
+ width: 52px;
|
|
|
+ height: 24px;
|
|
|
+ padding-top: 3px;
|
|
|
+ span {
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ .btn {
|
|
|
+ margin-left: 2px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .delivered-info {
|
|
|
+ position: absolute;
|
|
|
+ margin-top: 8px;
|
|
|
+ width: 100%;
|
|
|
+ .delivered-item {
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: var(--color-table-header-bg);
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+ .top {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ height: 58px;
|
|
|
+ border-bottom: 1px solid var(--color-border);
|
|
|
+ .top-left {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+ padding: 8px;
|
|
|
+ border-right: 1px solid var(--color-border);
|
|
|
+ .label {
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ .date {
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .top-right {
|
|
|
+ padding: 0 23px;
|
|
|
+ text-align: center;
|
|
|
+ font-weight: 700;
|
|
|
+ color: var(--color-neutral-1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .bottom {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 32px;
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
& > .line {
|
|
|
- height: 72px;
|
|
|
margin-left: 7px;
|
|
|
border-left: 1px solid var(--color-neutral-1);
|
|
|
}
|
|
|
+ & > .place-of-delivery-line {
|
|
|
+ margin-left: 7px;
|
|
|
+ // border-left: 1px dashed var(--color-neutral-3);
|
|
|
+ .dashed-line {
|
|
|
+ height: 50px;
|
|
|
+ border-left: 1px dashed var(--color-neutral-2);
|
|
|
+ }
|
|
|
+ }
|
|
|
&.last {
|
|
|
& > .data {
|
|
|
.left-step-icon {
|