Преглед на файлове

Merge branch 'test_zyh' into feat_download_zyh

Jack Zhou преди 1 месец
родител
ревизия
0165931ad4
променени са 20 файла, в които са добавени 402 реда и са изтрити 106 реда
  1. 1 1
      package.json
  2. 0 1
      src/api/module/login.ts
  3. 183 4
      src/components/ShipmentStatus/src/ShipmentStatus.vue
  4. 2 2
      src/components/TransportMode/src/TransportMode.vue
  5. 14 0
      src/stores/modules/notificationMessage.ts
  6. 1 0
      src/stores/modules/user.ts
  7. 1 1
      src/views/AIApiLog/src/components/LogDialog.vue
  8. 2 0
      src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue
  9. 14 1
      src/views/Dashboard/src/DashboardView.vue
  10. 15 3
      src/views/Dashboard/src/components/DashFiters.vue
  11. 13 7
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue
  12. 16 15
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue
  13. 55 44
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue
  14. 0 1
      src/views/DestinationDelivery/src/components/ModifyBooking/src/ModifyBooking.vue
  15. 4 4
      src/views/DestinationDelivery/src/components/TableView/src/TableView.vue
  16. 29 1
      src/views/Layout/src/components/Header/components/TrainingCard.vue
  17. 28 13
      src/views/SystemSettings/src/SystemSettings.vue
  18. 20 8
      src/views/SystemSettings/src/components/PersonalProfile.vue
  19. 2 0
      src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue
  20. 2 0
      src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue

+ 1 - 1
package.json

@@ -72,7 +72,7 @@
     "postcss": "^8.4.41",
     "postcss-loader": "^8.1.1",
     "prettier": "^3.2.5",
-    "rollup-plugin-visualizer": "^6.0.3",
+    "rollup-plugin-visualizer": "^6.0.4",
     "sass": "^1.79.4",
     "typescript": "~5.4.0",
     "unplugin-auto-import": "^0.18.2",

+ 0 - 1
src/api/module/login.ts

@@ -106,7 +106,6 @@ export const resetAndActivatePassword = (params: any, config: any) => {
   )
 }
 
-
 /**
  * 获取public tracking detail详情数据
  */

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

+ 2 - 2
src/components/TransportMode/src/TransportMode.vue

@@ -308,7 +308,7 @@ const defaultTransport = () => {
   padding-left: 16px;
 }
 .checkbox {
-  width: 216px;
+  width: 100%;
   font-weight: 400;
   font-size: var(--font-size-3);
   padding: 0 12px;
@@ -319,7 +319,7 @@ const defaultTransport = () => {
   width: 0;
 }
 :deep(.el-checkbox__label) {
-  width: 176px;
+  width: 100%;
   padding-left: 13px;
   display: flex;
   align-items: center;

+ 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 = []

+ 1 - 0
src/stores/modules/user.ts

@@ -15,6 +15,7 @@ interface UserInfo {
   numbers_format: string
   PASSWORD_CHANGE_CYCLE: number // 密码修改周期(多少天需要改一次)
   last_pwd_change: string // 上次密码修改时间
+  is_desensitization_kln?: string // Mask Customer Information
 }
 interface UserState {
   userInfo: UserInfo

+ 1 - 1
src/views/AIApiLog/src/components/LogDialog.vue

@@ -91,4 +91,4 @@ defineExpose({
     background-color: var(--color-json-item-hover);
   }
 }
-</style>
+</style>

+ 2 - 0
src/views/Booking/src/components/BookingDetail/src/BookingDetail.vue

@@ -374,6 +374,8 @@ const { isOverflow: isDestinationOverflow } = useOverflow(destinationRef, allDat
     color: var(--color-neutral-2);
   }
   .content {
+    width: 100%;
+    word-wrap: break-word;
     min-height: 16px;
     font-size: 14px;
     font-weight: 700;

+ 14 - 1
src/views/Dashboard/src/DashboardView.vue

@@ -1545,7 +1545,10 @@ function handleImageClick(event) {
 }
 .filters_left {
   border-radius: var(--border-radius-6);
-  flex: 1 40%;
+  width: calc(50% - 4px);
+  flex: 0 0 calc(50% - 4px);
+  min-width: 0;
+  box-sizing: border-box;
 }
 .KPI_title {
   border-bottom: 1px solid var(--color-border);
@@ -1576,6 +1579,16 @@ function handleImageClick(event) {
 .echarts {
   padding: 0 22px;
   background-color: var(--color-mode);
+  :deep(> div) {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    gap: 8px;
+    width: 100%;
+    > * {
+      box-sizing: border-box;
+    }
+  }
 }
 .kpi {
   width: 50%;

+ 15 - 3
src/views/Dashboard/src/components/DashFiters.vue

@@ -46,7 +46,7 @@ const checkboxGroup1 = ref(['All'])
 const CheckboxGroup2 = ref('ETD')
 const CheckboxGroup3 = ref('invoice Issue Date')
 const filters_visible = ref(false)
-const shipper = ref(['All', 'Air', 'Sea', 'Road'])
+const shipper = ref(['All', 'Air', 'Sea', 'Road', 'Rail'])
 const shipper_two = ref(['ETD', 'ETA'])
 const shipper_three = ref(['invoice Issue Date'])
 const DashDate = ref()
@@ -206,14 +206,14 @@ const guideStore = useGuideStore()
         </el-button>
       </template>
       <div class="Dash_title">Transport Mode</div>
-      <div class="filter_filter">
+      <div class="filter_filter_one">
         <el-checkbox-group
           @change="changeCheckboxGroup1"
           v-model="checkboxGroup1"
           size="large"
           :disabled="checkboxDisabled"
         >
-          <el-checkbox-button v-for="item in shipper" :key="item" :value="item">
+          <el-checkbox-button class="filter_button" v-for="item in shipper" :key="item" :value="item">
             {{ item }}
           </el-checkbox-button>
         </el-checkbox-group>
@@ -373,9 +373,21 @@ const guideStore = useGuideStore()
   justify-content: center;
   margin: 8px 16px 0 16px;
 }
+.filter_filter_one {
+  margin: 0 16px;
+  :deep(.el-checkbox-group) {
+    display: flex;
+  }
+}
 .filter_filter {
   margin-left: 16px;
 }
+.filter_button {
+  width: 20%;
+  :deep(.el-checkbox-button__inner) {
+    width: 100%;
+  }
+}
 .dash_flex {
   display: flex;
   align-items: center;

+ 13 - 7
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue

@@ -101,6 +101,7 @@ const CreateRuleDisabled = computed(() => {
     }
 
     // 3.2 验证航空规则(如果选择了 Air)
+    console.log(recommendCheckedList.value)
     if (recommendCheckedList.value.includes('Air')) {
       const isAirValid = recommendCheckedAirList.value.every(item => 
         item.ports.length > 0 && 
@@ -113,14 +114,19 @@ const CreateRuleDisabled = computed(() => {
 
     // 3.3 验证海运规则(如果选择了 Sea)
     if (recommendCheckedList.value.includes('Sea')) {
-      const isSeaValid = recommendCheckedSeaList.value.every(item => 
-        item.ports.length > 0 && 
-        item.carrier.length > 0 && 
-        item.recommended_delivery_from != '' && 
-        item.recommended_delivery_to != ''
-      );
+      const hasInvalidSeaItem = recommendCheckedSeaList.value.some(item => {
+        const hasValidDeliveryTime = 
+          item.recommended_delivery_from && 
+          item.recommended_delivery_to;
+        
+        const hasRequiredFields = item.rule_type !== 'Single Dimension'
+          ? item.ports.length > 0 && item.carrier.length > 0
+          : item.ports.length > 0 || item.carrier.length > 0;
+        
+        return !hasValidDeliveryTime || !hasRequiredFields;
+      });
       
-      if (!isSeaValid) return true;
+      if (hasInvalidSeaItem) return true;
     }
   }
   // 4. 所有条件都满足,返回 false(不禁用)

+ 16 - 15
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue

@@ -35,16 +35,19 @@ const value = ref<string[]>(props.SelectedValue)
 const SelectNumber = ref(props.SelectIndex)
 const typeisDisabled = ref(props.typeisDisabled)
 const PortList = ref(props.PortList)
+let isUpdating = false
 
 watch(
   () => props.SelectedValue,
   (val) => {
+    if (isUpdating || JSON.stringify(val) === JSON.stringify(value.value)) return
+    isUpdating = true
     value.value = val
-    if(val[0] == 'ALL') {
-      checkAll.value = true
-    }
-  }
-, { immediate: true, deep: true })
+    checkAll.value = val[0] === 'ALL'
+    isUpdating = false
+  },
+  { immediate: true, deep: true }
+)
 
 watch(
   () => props.PortList,
@@ -113,17 +116,15 @@ const remoteMethod = (query: string) => {
 const emits = defineEmits(['changeSelectedValue', 'handelremovetag'])
 
 watch(value, (val) => {
-  if (val.length === 0) {
-    checkAll.value = false
-  } else if (val.length === PortList.value.length) {
-    checkAll.value = true
-    value.value = ['ALL']
-  }else if (val.length == 1 && val[0] == 'ALL') {
-    checkAll.value = true
-  } else {
-    checkAll.value = false
-  }
+  if (isUpdating) return
+  isUpdating = true
+  
+  const allChecked = val.length === PortList.value.length || 
+                    (val.length === 1 && val[0] === 'ALL')
+  checkAll.value = allChecked
+  
   emits('changeSelectedValue', val, SelectNumber.value)
+  isUpdating = false
 })
 
 const handleCheckAll = (val) => {

+ 55 - 44
src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue

@@ -8,6 +8,7 @@ import NotShipment from './images/default_no_shipment@2x.png'
 import submitsucessful from './images/icon_success_big@2x.png'
 import { useUserStore } from '@/stores/modules/user'
 import { useRouter } from 'vue-router'
+import { ElMessage } from 'element-plus'
 
 const userStore = useUserStore()
 const router = useRouter()
@@ -26,6 +27,7 @@ const VesselName = ref([])
 const VesselNametest = ref('')
 const ShipperValue = ref('')
 const ConsigneeValue = ref('')
+const DeliveryReference = ref('')
 const getAddressListData = ref({})
 // const isFocused = ref(false)
 const isFocused = ref({
@@ -221,48 +223,39 @@ const Countryloading = ref(false)
 const city = ref<CountryItem[]>([])
 const Cityoptions = ref<CountryItem[]>([])
 const cityloading = ref(false)
-const getAddressCountryCityData = (type: any) => {
-  $api
+const querySearchCountry = (query: string) => {
+  Countryloading.value = true
+  setTimeout(() => {
+    $api
     .getAddressCountryCityData({
-      term: '',
-      term_type: type,
-      limit: type == 'country' ? CityCode.value : CountryCode.value
+      term: query,
+      term_type: 'country',
+      limit: CityCode.value != ''  ? CityCode.value : ''
     })
     .then((res: any) => {
       if (res.code === 200) {
-        if (type == 'country') {
-          countrys.value = res.data
-        } else {
-          city.value = res.data
-        }
+        Countryoptions.value = res.data
+        Countryloading.value = false
       }
     })
-}
-const querySearchCountry = (query: string) => {
-  if (query) {
-    Countryloading.value = true
-    setTimeout(() => {
-      Countryloading.value = false
-      Countryoptions.value = countrys.value.filter((item) => {
-        return item.label.toLowerCase().includes(query.toLowerCase())
-      })
-    }, 1000)
-  } else {
-    Countryoptions.value = []
-  }
+  }, 1000)
 }
 const querySearchCity = (query: string) => {
-  if (query) {
-    cityloading.value = true
-    setTimeout(() => {
-      cityloading.value = false
-      Cityoptions.value = city.value.filter((item) => {
-        return item.label.toLowerCase().includes(query.toLowerCase())
-      })
-    }, 1000)
-  } else {
-    Cityoptions.value = []
-  }
+  cityloading.value = true
+  setTimeout(() => {
+    $api
+    .getAddressCountryCityData({
+      term: query,
+      term_type: 'city',
+      limit: CountryCode.value != ''  ? CountryCode.value : ''
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        Cityoptions.value = res.data
+        cityloading.value = false
+      }
+    })
+  }, 1000)
 }
 // 特殊日期样式
 const getCurrentStyle = (current: any) => {
@@ -317,15 +310,15 @@ const AddNewAddressDelivery = () => {
 // 保存新地址
 const SaveNewAddress = () => {
   if (
+    CountryCode.value != '' &&
+    CityCode.value != '' &&
+    PostalCode.value != '' &&
+    ContactPerson.value != '' &&
+    ContactNumber.value != '' &&
     AddressLine1.value != '' ||
     AddressLine2.value != '' ||
     AddressLine3.value != '' ||
-    (AddressLine4.value != '' &&
-      CountryCode.value != '' &&
-      CityCode.value != '' &&
-      PostalCode.value != '' &&
-      ContactPerson.value != '' &&
-      ContactNumber.value != '')
+    AddressLine4.value != ''
   ) {
     const addressData = {
       address_1: AddressLine1.value,
@@ -368,6 +361,11 @@ const SaveNewAddress = () => {
     }
     AddNewAddressVisible.value = false
     currentEditAddress.value = null
+  } else {
+    ElMessage({
+      message: 'Required fields not entered.',
+      type: 'warning'
+    })
   }
 }
 // 点击按钮
@@ -379,9 +377,9 @@ const changeAddressRadio = () => {
   ManageVisible.value = false
   if (Addressradio.value != undefined) {
     isselectedAddress.value = Addressradio.value
+    selectedAddressList.value = ManageAddressList.value[Addressradio.value]
+    delivery_address = `${selectedAddressList.value.contact_person}(${selectedAddressList.value.contact_number})\n${selectedAddressList.value.address_1}\n${selectedAddressList.value.address_2}\n${selectedAddressList.value.address_3}\n${selectedAddressList.value.address_4},${selectedAddressList.value.country},${selectedAddressList.value.city},${selectedAddressList.value.postal_code}`
   }
-  selectedAddressList.value = ManageAddressList.value[Addressradio.value]
-  delivery_address = `${selectedAddressList.value.contact_person}(${selectedAddressList.value.contact_number})\n${selectedAddressList.value.address_1}\n${selectedAddressList.value.address_2}\n${selectedAddressList.value.address_3}\n${selectedAddressList.value.address_4},${selectedAddressList.value.country},${selectedAddressList.value.city},${selectedAddressList.value.postal_code}`
 }
 // 页面初始化
 let checkShipments = []
@@ -411,6 +409,7 @@ const getInitBookingData = () => {
             DeliveryTime.value = res.data.data.delivery_time
             Modification.value = res.data.data.modify_reason
             selectedAddressList.value = res.data.data.delivery_address_detail
+            DeliveryReference.value = res.data.data.delivery_reference
             isselectedAddress.value = ''
             const sync_key = res.data.data.delivery_address_detail.sync_key
             checkShipments = res.data.data.tableData.map((item) => ({
@@ -522,6 +521,7 @@ const getDateRangeArray = (startDateStr, endDateStr) => {
   }
 }
 const selectChangeEvent = (val: any, date: any, submitInfo: any) => {
+  ManageAddressList.value = []
   getAddressListData.value = { ...val }
   checkShipmentsSubmitInfo.value = { ...submitInfo }
   if (date.length != 0) {
@@ -670,6 +670,7 @@ const SubmitBooking = () => {
       ...checkShipmentsSubmitInfoData,
       delivery_time: DeliveryTime.value,
       delivery_mode: modetypeValue.value,
+      delivery_reference: DeliveryReference.value,
       delivery_date: datetwo,
       delivery_address: delivery_address,
       special_requirements: Requirements.value,
@@ -936,6 +937,12 @@ onMounted(() => {
             placeholder="Please Select Time"
           ></el-time-select>
         </div>
+        <div style="margin-left: 12px;">
+          <div class="delivery_type_title">Delivery Reference</div>
+          <el-tooltip class="item" effect="dark" content="Reference to be quoted on arrival at the Warehouse/DC" placement="bottom">
+            <el-input v-model="DeliveryReference" class="delivery_reference"></el-input>
+          </el-tooltip>
+        </div>
       </div>
       <div class="delivery_type_title">Special Requirements</div>
       <div class="flex" style="margin-top: 8px">
@@ -1148,7 +1155,6 @@ onMounted(() => {
             v-model="CountryCode"
             filterable
             remote
-            @focus="getAddressCountryCityData('country')"
             placeholder="Select Country"
             :remote-method="querySearchCountry"
             :loading="Countryloading"
@@ -1168,7 +1174,6 @@ onMounted(() => {
             v-model="CityCode"
             filterable
             remote
-            @focus="getAddressCountryCityData('city')"
             placeholder="Select City"
             :remote-method="querySearchCity"
             :loading="cityloading"
@@ -1599,6 +1604,12 @@ onMounted(() => {
 :deep(.el-input__wrapper) {
   height: 40px;
 }
+.delivery_reference {
+  width: 240px;
+  :deep(.el-input__wrapper) {
+    height: 32px;
+  }
+}
 .inputmargin {
   margin: 4px 0 16px 0;
 }

+ 0 - 1
src/views/DestinationDelivery/src/components/ModifyBooking/src/ModifyBooking.vue

@@ -97,7 +97,6 @@ const handleCancel = () => {
           </div>
           <div class="filetr-item mode-type">
             <div class="label"><span class="require-icon">*</span>Preferred Delivery Date</div>
-
             <el-date-picker v-model="deliveryDate" type="date" placeholder="Pick a day" />
           </div>
           <div class="special-requirements">

+ 4 - 4
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -59,7 +59,7 @@ const handleColumns = (columns: any, status?: string) => {
           if (!cellValue) return '--'
           const rangeData = cellValue.split(';')
           const leftDate = rangeData[0] ? formatTimezone(rangeData[0]) : '_'
-          const rightDate = rangeData[1] ? formatTimezone(rangeData[0]) : '_'
+          const rightDate = rangeData[1] ? formatTimezone(rangeData[1]) : '_'
           return leftDate + ' -- ' + rightDate
         }
       }
@@ -394,7 +394,7 @@ defineExpose({
           v-if="
             !isEmployeeRole &&
             row.status === 'Approved' &&
-            row.modify_by === userStore.userInfo.uname
+            row.create_by === userStore.userInfo.uname
           "
           @click="clickEmailBtn(row)"
           class="action-btn el-button--blue"
@@ -410,7 +410,7 @@ defineExpose({
           v-if="
             !isEmployeeRole &&
             (row.status === 'Pending Approval' || row.status === 'Rejected') &&
-            row.modify_by === userStore.userInfo.uname
+            row.create_by === userStore.userInfo.uname
           "
         >
           <span class="font_family icon-icon_edit_b"> </span>
@@ -423,7 +423,7 @@ defineExpose({
           v-if="
             !isEmployeeRole &&
             row.status === 'Pending Approval' &&
-            row.modify_by === userStore.userInfo.uname
+            row.create_by === userStore.userInfo.uname
           "
         >
           <span class="font_family icon-icon_cancelled_b"> </span>

+ 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>

+ 28 - 13
src/views/SystemSettings/src/SystemSettings.vue

@@ -137,15 +137,19 @@ const AddRulesTable = ref()
 const subscribeInit = ref({})
 
 const DefaultTimeZone = ref()
-const isInitSystem = JSON.parse(localStorage.getItem('userInfo')).subscribe_notification_default_init
+const isInitSystem = JSON.parse(
+  localStorage.getItem('userInfo')
+).subscribe_notification_default_init
 DefaultTimeZone.value = 'UTC' + moment().tz(moment.tz.guess()).format('Z')
 const FirstInitSubscribe = () => {
-  if(isInitSystem) {
-    $api.FirstInitSubscribe({
-      default_time_zone: DefaultTimeZone.value
-    }).then((res:any) => {
-      getsubscribe()
-    })
+  if (isInitSystem) {
+    $api
+      .FirstInitSubscribe({
+        default_time_zone: DefaultTimeZone.value
+      })
+      .then((res: any) => {
+        getsubscribe()
+      })
   } else {
     getsubscribe()
   }
@@ -166,7 +170,7 @@ const getsubscribe = () => {
 }
 
 const handleTabClick = (tab: any) => {
-  if(tab.paneName == 'Subscribe Notifications') {
+  if (tab.paneName == 'Subscribe Notifications') {
     isMilestoneChecked.value = false
     isContainerChecked.value = false
     isDepartureChecked.value = false
@@ -266,7 +270,11 @@ onMounted(() => {
               ></AddRules>
             </div>
           </el-collapse-item>
-          <el-collapse-item style="margin: 8px 0;" name="Container" :class="isContainerChecked ? 'border_ischecked' : ''">
+          <el-collapse-item
+            style="margin: 8px 0"
+            name="Container"
+            :class="isContainerChecked ? 'border_ischecked' : ''"
+          >
             <template #title>
               <div class="flex">
                 <div class="collapse_left" :class="isContainerAdded ? 'text_ischecked' : ''">
@@ -286,7 +294,11 @@ onMounted(() => {
               ></AddRules>
             </div>
           </el-collapse-item>
-          <el-collapse-item style="margin-bottom: 8px;" name="Departure" :class="isDepartureChecked ? 'border_ischecked' : ''">
+          <el-collapse-item
+            style="margin-bottom: 8px"
+            name="Departure"
+            :class="isDepartureChecked ? 'border_ischecked' : ''"
+          >
             <template #title>
               <div class="flex">
                 <div class="collapse_left" :class="isDepartureAdded ? 'text_ischecked' : ''">
@@ -377,6 +389,9 @@ onMounted(() => {
 .SubscribeCollapse {
   padding: 0 24px 24px 24px;
   border-bottom: 1px solid var(--color-select-border);
+  :deep(.el-icon svg) {
+    width: 0;
+  }
 }
 :deep(.el-tabs__nav-wrap:after) {
   height: 2px;
@@ -471,9 +486,9 @@ onMounted(() => {
 :deep(.el-collapse-item__arrow.is-active) {
   transform: rotate(-180deg);
 }
-:deep(.el-icon svg) {
-  width: 0;
-}
+// :deep(.el-icon svg) {
+//   width: 0;
+// }
 .monitoring_flex {
   display: flex;
   justify-content: space-between;

+ 20 - 8
src/views/SystemSettings/src/components/PersonalProfile.vue

@@ -10,6 +10,7 @@ const form = reactive({
   lastName: userStore.userInfo?.last_name,
   username: userStore.userInfo?.uname,
   email: userStore.userInfo?.email,
+  is_desensitization_kln: userStore.userInfo?.is_desensitization_kln || 'f',
   password: '**************'
 })
 
@@ -92,6 +93,12 @@ const saveConfig = (model: string) => {
       numbers_format: numbersFormat.value
     }
   }
+  if (userStore.userInfo?.user_type === 'employee') {
+    params = {
+      ...params,
+      is_desensitization_kln: form.is_desensitization_kln
+    }
+  }
   $api
     .saveUserInfo(params)
     .then((res: any) => {
@@ -109,6 +116,9 @@ const saveConfig = (model: string) => {
                 numbers_format: numbersFormat.value
               }
 
+        if (userStore.userInfo.user_type === 'employee' && model === 'profile') {
+          updatedInfo.is_desensitization_kln = form.is_desensitization_kln
+        }
         userStore.setUserInfo(updatedInfo)
         ElMessage.success('Save successfully')
       } else {
@@ -157,13 +167,7 @@ const saveConfig = (model: string) => {
               >
             </p>
             <div class="password-change">
-              <el-input
-                size="large"
-                type="password"
-                style="width: 330px"
-                :disabled="true"
-                v-model="form.password"
-              />
+              <el-input size="large" type="password" :disabled="true" v-model="form.password" />
               <el-button
                 @click="handleChangePassword"
                 class="el-button--main el-button--pain-theme"
@@ -173,6 +177,13 @@ const saveConfig = (model: string) => {
               >
             </div>
           </div>
+          <div class="item" v-if="userStore.userInfo?.user_type === 'employee'">
+            <p class="label">Mask Customer Information</p>
+            <el-select size="large" v-model="form.is_desensitization_kln">
+              <el-option label="Yes" value="t"></el-option>
+              <el-option label="No" value="f"></el-option>
+            </el-select>
+          </div>
         </div>
         <div class="row">
           <el-button @click="saveConfig('profile')" class="el-button--dark save-icon" size="large"
@@ -211,7 +222,7 @@ const saveConfig = (model: string) => {
                   <template #default>
                     <span>{{ key }}</span>
                     <el-select
-                      style="margin-left: 28px; width: 320px"
+                      style="margin-left: 28px; width: 320px; height: 42px"
                       v-if="dateFormat === key"
                       size="large"
                       v-model="monthFormat"
@@ -301,6 +312,7 @@ const saveConfig = (model: string) => {
       gap: 8px;
       .item {
         flex: 1;
+        max-width: calc(50% - 4px);
         margin-bottom: 16px;
         .label {
           margin-bottom: 4px;

+ 2 - 0
src/views/Tracking/src/components/PublicTracking/src/components/PublicTrackingDetail.vue

@@ -348,6 +348,8 @@ const openShareDialog = () => {
     color: var(--color-neutral-2);
   }
   .content {
+    width: 100%;
+    word-wrap: break-word;
     min-height: 16px;
     font-size: 14px;
     font-weight: 700;

+ 2 - 0
src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue

@@ -553,6 +553,8 @@ const SubscribeShipments = () => {
     color: var(--color-neutral-2);
   }
   .content {
+    width: 100%;
+    word-wrap: break-word;
     min-height: 16px;
     font-size: 14px;
     font-weight: 700;