Ver Fonte

Merge branch 'master_zyh' of United_Software/k_online_ui into master

Jack Zhou há 1 mês atrás
pai
commit
cd298ffa89

BIN
dist.rar


+ 183 - 4
src/components/ShipmentStatus/src/ShipmentStatus.vue

@@ -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 {

+ 14 - 0
src/stores/modules/notificationMessage.ts

@@ -59,6 +59,20 @@ export const useNotificationMessage = defineStore('notificationMessage', {
         }
       })
     },
+    // 在轮播后将消息置为已读
+    async markMessageAsReadAfterCarousel(displayedIds: string[]) {
+      if (displayedIds.length === 0) return
+
+      await $api.setMessageRead({ id: displayedIds }).then((res) => {
+        if (res.code === 200) {
+          this.readCardMap = []
+          localStorage.setItem('readCardMap', JSON.stringify(this.readCardMap))
+
+          // 在将消息标记为已读后,再次检查是否有新消息
+          this.hasUnreadMessages()
+        }
+      })
+    },
     clearData() {
       this.notificationMsgList = []
       this.readCardMap = []

+ 29 - 1
src/views/Layout/src/components/Header/components/TrainingCard.vue

@@ -11,7 +11,18 @@ const notificationMsgStore = useNotificationMessage()
 const notificationList = ref([])
 
 const curIndex = ref(0)
+const displayedList = ref([])
+const isNewCard = (id) => {
+  if (!id) return false
+  return !displayedList.value.some((item) => item.info.id === id)
+}
 const curCard = computed(() => {
+  if (
+    notificationList.value[curIndex.value] &&
+    isNewCard(notificationList.value[curIndex.value].info.id)
+  ) {
+    displayedList.value.push(notificationList.value[curIndex.value])
+  }
   return notificationList.value[curIndex.value] || null
 })
 
@@ -35,6 +46,11 @@ const trainingCardAfterLogin = () => {
   // 如果消息展示完毕,清除定时器
   if (curIndex.value >= notificationList.value.length) {
     clearInterval(trainingIntervalId)
+    // 将已经展示的消息标记为已读
+    notificationMsgStore.markMessageAsReadAfterCarousel(
+      displayedList.value.map((item) => item.info.id)
+    )
+    displayedList.value = []
   }
 }
 
@@ -54,6 +70,12 @@ const nextNotification = () => {
   if (curIndex.value >= notificationList.value.length) {
     clearInterval(trainingIntervalId)
     result = false
+
+    // 将已经展示的消息标记为已读
+    notificationMsgStore.markMessageAsReadAfterCarousel(
+      displayedList.value.map((item) => item.info.id)
+    )
+    displayedList.value = []
   }
   return result
 }
@@ -104,6 +126,12 @@ const getNotificationList = (time?: string) => {
 
 // 关闭消息,如果是登录后自动轮播的消息,则需清除定时器,如果不是则继续轮播下一条消息
 const closeMessage = () => {
+  // 将已经展示的消息标记为已读
+  notificationMsgStore.markMessageAsReadAfterCarousel(
+    displayedList.value.map((item) => item.info.id)
+  )
+  displayedList.value = []
+
   if (!newMessageIntervalId) {
     clearInterval(trainingIntervalId)
     curIndex.value = -1
@@ -132,9 +160,9 @@ const closeMessage = () => {
       <span class="font_family icon-icon_reject_b"></span>
     </el-button>
     <NotificationMessageCard
-      :isObserver="false"
       v-if="curCard"
       :data="[curCard]"
+      :isObserver="false"
       :topOffset="0"
       :isDrawer="true"
     ></NotificationMessageCard>