Bladeren bron

feat: 实现登录后弹窗轮播未读消息

zhouyuhao 8 maanden geleden
bovenliggende
commit
535098375c

+ 28 - 2
src/api/module/notificationMessage.ts

@@ -20,8 +20,9 @@ export const saveUserInfo = (params: any) => {
 }
 }
 
 
 /**
 /**
- * 获取milestone消息列表
- * @param save_model profile 代表基本信息的save, no_profile 代表格式信息的save
+ * 获取notification消息列表
+ * @param rules_type 特定类型的消息,all查全部
+ * @param current_time 当前时间 轮询查询时使用
  */
  */
 export const getNotificationList = (params: any) => {
 export const getNotificationList = (params: any) => {
   return HttpAxios.get(`${baseUrl}`, {
   return HttpAxios.get(`${baseUrl}`, {
@@ -41,3 +42,28 @@ export const getNotificationDetails = (params: any) => {
     ...params
     ...params
   })
   })
 }
 }
+
+/**
+ * 获取system message页面数据
+ */
+export const getSystemMessageData = (params: any) => {
+  return HttpAxios.get(`${baseUrl}`, {
+    action: 'notifications_rules',
+    operate: 'notifications_message_init',
+    ...params
+  })
+}
+
+/**
+ * 将notification消息标记为已读
+ * @param id 消息id Array
+ * @param read_type 如果标记全部为已读 = true. 否则其他情况为false
+ */
+export const setMessageRead = (params: any) => {
+  return HttpAxios.post(`${baseUrl}`, {
+    action: 'notifications_rules',
+    operate: 'notifications_read',
+    read_type: false,
+    ...params
+  })
+}

+ 2 - 1
src/auto-imports.d.ts

@@ -3,6 +3,7 @@
 // @ts-nocheck
 // @ts-nocheck
 // noinspection JSUnusedGlobalSymbols
 // noinspection JSUnusedGlobalSymbols
 // Generated by unplugin-auto-import
 // Generated by unplugin-auto-import
+// biome-ignore lint: disable
 export {}
 export {}
 declare global {
 declare global {
   const $api: typeof import('@/api/index')['default']
   const $api: typeof import('@/api/index')['default']
@@ -68,6 +69,6 @@ declare global {
 // for type re-export
 // for type re-export
 declare global {
 declare global {
   // @ts-ignore
   // @ts-ignore
-  export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
+  export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
   import('vue')
   import('vue')
 }
 }

+ 10 - 2
src/components/NotificationMessageCard/src/NotificationMessageCard.vue

@@ -6,13 +6,21 @@ import FeatureUpdateCard from './components/FeatureUpdateCard.vue'
 const props = defineProps<{
 const props = defineProps<{
   data: any
   data: any
 }>()
 }>()
-console.log(props.data, 'props.data')
+
+const emit = defineEmits<{ seeAll: [] }>()
+const handleSeeAll = () => {
+  emit('seeAll')
+}
 </script>
 </script>
 
 
 <template>
 <template>
   <div class="notification-message-card">
   <div class="notification-message-card">
     <template v-for="(item, index) in data" :key="index">
     <template v-for="(item, index) in data" :key="index">
-      <EventCard v-if="item.notificationType === 'event'" :data="item.info" />
+      <EventCard
+        @seeAll="handleSeeAll"
+        v-if="item.notificationType === 'event'"
+        :data="item.info"
+      />
       <PasswordCard v-else-if="item.notificationType === 'password'" :data="item.info" />
       <PasswordCard v-else-if="item.notificationType === 'password'" :data="item.info" />
       <FeatureUpdateCard v-else-if="item.notificationType === 'feature'" :data="item.info" />
       <FeatureUpdateCard v-else-if="item.notificationType === 'feature'" :data="item.info" />
     </template>
     </template>

+ 19 - 32
src/components/NotificationMessageCard/src/components/EventCard.vue

@@ -20,6 +20,9 @@ interface EventCardPropsData {
   timeLabel: string
   timeLabel: string
   serial_no?: string // 单号 用来跳转到Tracking详情页
   serial_no?: string // 单号 用来跳转到Tracking详情页
   order_from?: string // 订单来源 用来跳转到Tracking详情页
   order_from?: string // 订单来源 用来跳转到Tracking详情页
+  insert_date_format?: string // 用来跳转到System Message详情页
+  frequency_type?: string // 用来跳转到System Message详情页
+  rules_type?: string // 用来跳转到System Message详情页
   previous?: {
   previous?: {
     date: string
     date: string
     tag: string
     tag: string
@@ -42,16 +45,20 @@ const props = defineProps<{
   data: EventCardPropsData
   data: EventCardPropsData
 }>()
 }>()
 
 
-const handleSeeAll = (data: any) => {
+const emit = defineEmits<{ seeAll: [] }>()
+const handleSeeAll = (data: EventCardPropsData) => {
+  emit('seeAll')
   router.push({
   router.push({
     name: 'System Message Detail',
     name: 'System Message Detail',
     query: {
     query: {
-      frequency_type: data.frequency_type
+      frequency_type: data.frequency_type,
+      insert_date_format: data.insert_date_format,
+      rules_type: data.rules_type
     }
     }
   })
   })
 }
 }
 
 
-const jumpTracking = (data: any) => {
+const jumpTracking = (data: EventCardPropsData) => {
   router.push({
   router.push({
     path: '/tracking/detail',
     path: '/tracking/detail',
     query: { a: data.serial_no, _schemas: data.order_from }
     query: { a: data.serial_no, _schemas: data.order_from }
@@ -76,40 +83,20 @@ const jumpTracking = (data: any) => {
           <span class="font_family icon-icon_next_b"></span>
           <span class="font_family icon-icon_next_b"></span>
         </el-button>
         </el-button>
       </div>
       </div>
-      <div
-        class="more-tips"
-        v-if="data.type === 'delay' && (data.info.etdOrdeparturNum || data.info.etaOrarrivalNum)"
-      >
-        <div>
-          <span v-if="data.info.etdOrdeparturNum"
-            >Departure Delay ({{ data.info.etdOrdeparturNum }})</span
-          >
-          <span v-if="data.info.etdOrdeparturNum && data.info.etaOrarrivalNum">
-            &nbsp;&nbsp;|&nbsp;&nbsp;</span
-          >
-          <span v-if="data.info.etaOrarrivalNum">
-            Arrival Delay ({{ data.info.etaOrarrivalNum }})
-          </span>
-        </div>
-        <el-button @click="handleSeeAll(data)" class="see-all-icon el-button--text">
-          See All
-          <span class="font_family icon-icon_next_b"></span>
-        </el-button>
-      </div>
-
-      <div
-        class="more-tips"
-        v-if="data.type === 'change' && (data.info.etdOrdeparturNum || data.info.etaOrarrivalNum)"
-      >
+      <div class="more-tips" v-if="data.info.etdOrdeparturNum || data.info.etaOrarrivalNum">
         <div>
         <div>
           <span v-if="data.info.etdOrdeparturNum"
           <span v-if="data.info.etdOrdeparturNum"
-            >ETD Change ({{ data.info.etdOrdeparturNum }})</span
+            >{{ data.type === 'delay' ? 'Departure Delay' : 'ETD Change' }} ({{
+              data.info.etdOrdeparturNum
+            }})</span
           >
           >
           <span v-if="data.info.etdOrdeparturNum && data.info.etaOrarrivalNum">
           <span v-if="data.info.etdOrdeparturNum && data.info.etaOrarrivalNum">
             &nbsp;&nbsp;|&nbsp;&nbsp;</span
             &nbsp;&nbsp;|&nbsp;&nbsp;</span
           >
           >
           <span v-if="data.info.etaOrarrivalNum">
           <span v-if="data.info.etaOrarrivalNum">
-            ETA Change ({{ data.info.etaOrarrivalNum }})
+            {{ data.type === 'delay' ? 'Arrival Delay' : 'ETA Change' }} ({{
+              data.info.etaOrarrivalNum
+            }})
           </span>
           </span>
         </div>
         </div>
         <el-button @click="handleSeeAll(data)" class="see-all-icon el-button--text">
         <el-button @click="handleSeeAll(data)" class="see-all-icon el-button--text">
@@ -165,7 +152,7 @@ const jumpTracking = (data: any) => {
           class="font_family icon-icon_delay_b"
           class="font_family icon-icon_delay_b"
         ></span>
         ></span>
         <span v-else class="font_family icon-icon_time_b"></span>
         <span v-else class="font_family icon-icon_time_b"></span>
-        <span style="margin-right: 2px">{{ data.info.timeLabel }}&nbsp;</span>
+        <span style="margin-right: 2px" v-if="data.info.timeLabel">{{ data.info.timeLabel }}:</span>
         <span style="margin-right: 3px">{{
         <span style="margin-right: 3px">{{
           dayjs(data.info.time).format('MMM DD, YYYY hh:mm')
           dayjs(data.info.time).format('MMM DD, YYYY hh:mm')
         }}</span>
         }}</span>
@@ -177,7 +164,7 @@ const jumpTracking = (data: any) => {
       </div> -->
       </div> -->
       <div class="time" :class="{ grey: data.type === 'delay' || data.type === 'change' }">
       <div class="time" :class="{ grey: data.type === 'delay' || data.type === 'change' }">
         <span class="font_family icon-icon_time_b"></span>
         <span class="font_family icon-icon_time_b"></span>
-        <span style="margin-right: 2px">{{ data.timeLabel }}</span>
+        <span style="margin-right: 3px" v-if="data.timeLabel">{{ data.timeLabel }}:</span>
         <span style="margin-right: 3px">{{ dayjs(data.time).format('MMM DD, YYYY hh:mm') }}</span>
         <span style="margin-right: 3px">{{ dayjs(data.time).format('MMM DD, YYYY hh:mm') }}</span>
         <span>{{ getTimezone(data.timezone) }}</span>
         <span>{{ getTimezone(data.timezone) }}</span>
       </div>
       </div>

+ 0 - 1
src/components/NotificationMessageCard/src/components/FeatureUpdateCard.vue

@@ -12,7 +12,6 @@ interface FeatureUpdateCardPropsData {
 const props = defineProps<{
 const props = defineProps<{
   data: FeatureUpdateCardPropsData
   data: FeatureUpdateCardPropsData
 }>()
 }>()
-console.log(props.data, 'props.data111')
 </script>
 </script>
 
 
 <template>
 <template>

+ 2 - 1
src/router/index.ts

@@ -97,7 +97,8 @@ const router = createRouter({
           path: '/system-message-detail',
           path: '/system-message-detail',
           name: 'System Message Detail',
           name: 'System Message Detail',
           meta: {
           meta: {
-            breadName: 'Detail'
+            breadName: 'Detail',
+            activeMenu: '/system-message'
           },
           },
           component: () => import('../views/SystemMessage/src/components/SystemMessageDetail.vue')
           component: () => import('../views/SystemMessage/src/components/SystemMessageDetail.vue')
         },
         },

+ 3 - 1
src/views/Layout/src/components/Header/HeaderView.vue

@@ -17,6 +17,8 @@ const route = useRoute()
 const router = useRouter()
 const router = useRouter()
 const headerSearch = useHeaderSearch()
 const headerSearch = useHeaderSearch()
 
 
+const trainingCardRef = ref()
+
 // 切换系统主题颜色
 // 切换系统主题颜色
 const toggleThemeMode = (theme: string) => {
 const toggleThemeMode = (theme: string) => {
   themeStore.toggleTheme(theme, true)
   themeStore.toggleTheme(theme, true)
@@ -188,7 +190,7 @@ const notificationDrawer = ref(false)
     element-loading-background="rgb(43, 47, 54, 0.7)"
     element-loading-background="rgb(43, 47, 54, 0.7)"
   >
   >
     <VBreadcrumb></VBreadcrumb>
     <VBreadcrumb></VBreadcrumb>
-    <TrainingCard></TrainingCard>
+    <TrainingCard ref="trainingCardRef"></TrainingCard>
     <div class="right-info">
     <div class="right-info">
       <el-input
       <el-input
         v-model="searchValue"
         v-model="searchValue"

+ 92 - 82
src/views/Layout/src/components/Header/components/NotificationDrawer.vue

@@ -5,8 +5,6 @@ import { useRouter } from 'vue-router'
 const router = useRouter()
 const router = useRouter()
 const loading = ref(false)
 const loading = ref(false)
 
 
-const drawerModel = defineModel('drawerModel', { type: Boolean, default: false })
-
 const notificationType = ref('all')
 const notificationType = ref('all')
 const notificationTypeList = ref({
 const notificationTypeList = ref({
   all: 'All Notifications',
   all: 'All Notifications',
@@ -18,84 +16,84 @@ const notificationTypeList = ref({
   Feature_Update: 'Feature Update'
   Feature_Update: 'Feature Update'
 })
 })
 
 
-// const notificationList = ref<any[]>([])
-const notificationList = [
-  {
-    notificationType: 'feature',
-    info: {
-      isRead: true,
-      title: 'Feature Update',
-      header: 'New feature online: Quick search has been released!',
-      content:
-        'We are pleased to announce that the quick search function is now officially online! You can now quickly find what you need by entering keywords, greatly improving your work efficiency. Go and experience it!'
-    }
-  },
-  {
-    notificationType: 'event',
-    info: {
-      type: 'milestone',
-      isMultiple: true,
-      numericRecords: 3,
-      isRead: true,
-      title: 'Milestone Update',
-      mode: 'Ocean Freight',
-      no: 'HBOL: SHJN2301234',
-      tag: 'Booking Confirmed',
-      location: 'Hong Kong',
-      time: 'Jan 10, 2025 14:30 UTC+8',
-      info: {
-        route: ['Hong Kong', 'Shanghai', 'Ningbo']
-      }
-    }
-  },
-  {
-    notificationType: 'password',
-    info: {
-      title: 'Password Notifications',
-      isExpiration: true,
-      tips: 'Password Expiration in 311 Days',
-      content:
-        'Your password will expire in 7 days. To ensure the security of your account, please change your password as soon as possible.'
-    }
-  },
-  {
-    notificationType: 'password',
-    info: {
-      isRead: false,
-      title: 'Password Notifications',
-      isExpiration: false,
-      tips: 'Password Expiration in 3 Days',
-      content:
-        'Your password will expire in 7 days. To ensure the security of your account, please change your password as soon as possible.'
-    }
-  },
-  {
-    notificationType: 'password',
-    info: {
-      isRead: true,
-      title: 'Password Notifications',
-      isExpiration: true,
-      tips: 'Password Expiration in 31111 Days',
-      content:
-        'Your password will expire in 7 days. To ensure the security of your account, please change your password as soon as possible.'
-    }
-  }
-]
+const notificationList = ref<any[]>([])
+// const notificationList = [
+//   {
+//     notificationType: 'feature',
+//     info: {
+//       isRead: true,
+//       title: 'Feature Update',
+//       header: 'New feature online: Quick search has been released!',
+//       content:
+//         'We are pleased to announce that the quick search function is now officially online! You can now quickly find what you need by entering keywords, greatly improving your work efficiency. Go and experience it!'
+//     }
+//   },
+//   {
+//     notificationType: 'event',
+//     info: {
+//       type: 'milestone',
+//       isMultiple: true,
+//       numericRecords: 3,
+//       isRead: true,
+//       title: 'Milestone Update',
+//       mode: 'Ocean Freight',
+//       no: 'HBOL: SHJN2301234',
+//       tag: 'Booking Confirmed',
+//       location: 'Hong Kong',
+//       time: 'Jan 10, 2025 14:30 UTC+8',
+//       info: {
+//         route: ['Hong Kong', 'Shanghai', 'Ningbo']
+//       }
+//     }
+//   },
+//   {
+//     notificationType: 'password',
+//     info: {
+//       title: 'Password Notifications',
+//       isExpiration: true,
+//       tips: 'Password Expiration in 311 Days',
+//       content:
+//         'Your password will expire in 7 days. To ensure the security of your account, please change your password as soon as possible.'
+//     }
+//   },
+//   {
+//     notificationType: 'password',
+//     info: {
+//       isRead: false,
+//       title: 'Password Notifications',
+//       isExpiration: false,
+//       tips: 'Password Expiration in 3 Days',
+//       content:
+//         'Your password will expire in 7 days. To ensure the security of your account, please change your password as soon as possible.'
+//     }
+//   },
+//   {
+//     notificationType: 'password',
+//     info: {
+//       isRead: true,
+//       title: 'Password Notifications',
+//       isExpiration: true,
+//       tips: 'Password Expiration in 31111 Days',
+//       content:
+//         'Your password will expire in 7 days. To ensure the security of your account, please change your password as soon as possible.'
+//     }
+//   }
+// ]
 
 
 const getNotificationList = () => {
 const getNotificationList = () => {
-  // loading.value = true
-  // $api
-  //   .getNotificationList({
-  //     rules_type: notificationType.value
-  //   })
-  //   .then((res) => {
-  //     if (res.code === 200) {
-  //       notificationList.value = res.data
-  //     }
-  //   })
-  //   .finally(() => {
-  //     loading.value = false
-  //   })
+  loading.value = true
+  $api
+    .getNotificationList({
+      rules_type: notificationType.value
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        notificationList.value = res.data
+      }
+    })
+    .finally(() => {
+      loading.value = false
+    })
 }
 }
 
 
 const handleMarkAllRead = () => {
 const handleMarkAllRead = () => {
@@ -103,15 +101,27 @@ const handleMarkAllRead = () => {
   // notificationList.value.forEach((item) => {
   // notificationList.value.forEach((item) => {
   //   item.isRead = true
   //   item.isRead = true
   // })
   // })
+  $api.setMessageRead({ read_type: true }).then((res) => {
+    if (res.code === 200) {
+      console.log('标记成功')
+    }
+  })
 }
 }
 
 
+const drawerRef = ref()
 const handleViewAll = () => {
 const handleViewAll = () => {
+  drawerRef.value.handleClose()
   router.push('/system-message')
   router.push('/system-message')
 }
 }
 
 
 const handleSettingMessage = () => {
 const handleSettingMessage = () => {
-  // 跳转消息设置页面
-  // router.push('/')
+  drawerRef.value.handleClose()
+  router.push({
+    name: 'Monitoring Settings',
+    query: {
+      tab: 'Subscribe Notifications'
+    }
+  })
 }
 }
 
 
 const clearData = () => {
 const clearData = () => {
@@ -121,10 +131,10 @@ const clearData = () => {
 
 
 <template>
 <template>
   <el-drawer
   <el-drawer
+    ref="drawerRef"
     @open="getNotificationList"
     @open="getNotificationList"
     @closed="clearData"
     @closed="clearData"
     class="notice-drawer"
     class="notice-drawer"
-    v-model="drawerModel"
     size="432px"
     size="432px"
   >
   >
     <template #header>
     <template #header>
@@ -163,7 +173,7 @@ const clearData = () => {
           </div>
           </div>
         </div>
         </div>
         <div class="notification-content">
         <div class="notification-content">
-          <NotificationMessageCard :data="notificationList" />
+          <NotificationMessageCard @see-all="drawerRef.handleClose()" :data="notificationList" />
         </div>
         </div>
       </el-scrollbar>
       </el-scrollbar>
     </template>
     </template>

+ 33 - 12
src/views/Layout/src/components/Header/components/TrainingCard.vue

@@ -1,7 +1,10 @@
 <script setup lang="ts">
 <script setup lang="ts">
+import { onBeforeRouteUpdate } from 'vue-router'
 import NotificationMessageCard from '@/components/NotificationMessageCard/src/NotificationMessageCard.vue'
 import NotificationMessageCard from '@/components/NotificationMessageCard/src/NotificationMessageCard.vue'
+import { useUserStore } from '@/stores/modules/user'
 
 
-const notificationList = []
+const userStore = useUserStore()
+const notificationList = ref([])
 // const notificationList = [
 // const notificationList = [
 //   {
 //   {
 //     notificationType: 'feature',
 //     notificationType: 'feature',
@@ -104,15 +107,8 @@ const notificationList = []
 //   }
 //   }
 // ]
 // ]
 
 
-const getNotificationList = () => {
-  if (localStorage.getItem('showFeatureAfterLogin') !== 'true') return
-  // 获取数据
-
-  localStorage.removeItem('showFeatureAfterLogin')
-}
-
 const curCard = computed(() => {
 const curCard = computed(() => {
-  return notificationList[curIndex.value] || null
+  return notificationList.value[curIndex.value] || null
 })
 })
 const curIndex = ref(0)
 const curIndex = ref(0)
 // 设置定时器进行自动轮播
 // 设置定时器进行自动轮播
@@ -133,7 +129,7 @@ const nextNotification = () => {
   }
   }
 
 
   // 如果到达最后一个消息,设置为null以清除显示
   // 如果到达最后一个消息,设置为null以清除显示
-  if (curIndex.value >= notificationList.length) {
+  if (curIndex.value >= notificationList.value.length) {
     clearInterval(intervalId)
     clearInterval(intervalId)
     // curCard.value = null
     // curCard.value = null
     result = false
     result = false
@@ -146,7 +142,32 @@ const initTrainingCard = () => {
     intervalId = setInterval(nextNotification, 2000)
     intervalId = setInterval(nextNotification, 2000)
   }
   }
 }
 }
-initTrainingCard()
+
+onBeforeRouteUpdate((to, from, next) => {
+  if (from.name === 'Login' && userStore.userName) {
+    getNotificationList()
+  }
+  next()
+})
+
+const getNotificationList = () => {
+  // if (localStorage.getItem('showFeatureAfterLogin') !== 'true') return
+  // // 获取数据
+
+  // localStorage.removeItem('showFeatureAfterLogin')
+  $api
+    .getNotificationList({
+      rules_type: 'all',
+      info_type: true
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        notificationList.value = res.data
+        console.log(curCard.value, 'curCard')
+        initTrainingCard()
+      }
+    })
+}
 
 
 const closeMessage = () => {
 const closeMessage = () => {
   // 如果当前消息为event类型,则需先清除定时器
   // 如果当前消息为event类型,则需先清除定时器
@@ -179,7 +200,7 @@ const closeMessage = () => {
   position: absolute;
   position: absolute;
   top: 60px;
   top: 60px;
   right: 20px;
   right: 20px;
-  z-index: 2010;
+  z-index: 2300;
   width: 432px;
   width: 432px;
   padding: 16px;
   padding: 16px;
   padding-bottom: 0;
   padding-bottom: 0;

+ 47 - 47
src/views/Layout/src/components/Menu/MenuView.vue

@@ -17,54 +17,54 @@ watch(
   }
   }
 )
 )
 const getMenuList = () => {
 const getMenuList = () => {
-  $api.getMenuList().then((res) => {
-    if (res.code === 200) {
-      menuList.value = res.data
-    }
-  })
-  // menuList.value = [
-  //   {
-  //     index: '1',
-  //     label: 'Dashboard',
-  //     icon: 'icon_data_fill_b',
-  //     path: '/dashboard'
-  //   },
-  //   {
-  //     index: '2',
-  //     label: 'Booking',
-  //     icon: 'icon_booking__fill_b',
-  //     path: '/booking'
-  //   },
-  //   {
-  //     index: '3',
-  //     label: 'Tracking',
-  //     icon: 'icon_tracking__fill_b',
-  //     path: '/tracking'
-  //   },
-  //   {
-  //     index: '4',
-  //     label: 'System Management',
-  //     icon: 'icon_system__management_fill_b',
-  //     type: 'list',
-  //     children: [
-  //       {
-  //         index: '4-1',
-  //         label: 'System Message',
-  //         path: '/system-message'
-  //       },
-  //       {
-  //         index: '4-2',
-  //         label: 'System Settings',
-  //         path: '/SystemSettings'
-  //       },
-  //       {
-  //         index: '4-3',
-  //         label: 'Operation Log',
-  //         path: '/Operationlog'
-  //       }
-  //     ]
+  // $api.getMenuList().then((res) => {
+  //   if (res.code === 200) {
+  //     menuList.value = res.data
   //   }
   //   }
-  // ]
+  // })
+  menuList.value = [
+    {
+      index: '1',
+      label: 'Dashboard',
+      icon: 'icon_data_fill_b',
+      path: '/dashboard'
+    },
+    {
+      index: '2',
+      label: 'Booking',
+      icon: 'icon_booking__fill_b',
+      path: '/booking'
+    },
+    {
+      index: '3',
+      label: 'Tracking',
+      icon: 'icon_tracking__fill_b',
+      path: '/tracking'
+    },
+    {
+      index: '4',
+      label: 'System Management',
+      icon: 'icon_system__management_fill_b',
+      type: 'list',
+      children: [
+        {
+          index: '4-1',
+          label: 'System Message',
+          path: '/system-message'
+        },
+        {
+          index: '4-2',
+          label: 'System Settings',
+          path: '/SystemSettings'
+        },
+        {
+          index: '4-3',
+          label: 'Operation Log',
+          path: '/Operationlog'
+        }
+      ]
+    }
+  ]
 }
 }
 getMenuList()
 getMenuList()
 //监听窗口大小
 //监听窗口大小

+ 111 - 288
src/views/SystemMessage/src/SystemMessage.vue

@@ -1,13 +1,9 @@
 <script setup lang="ts">
 <script setup lang="ts">
-import EventCard from '@/components/NotificationMessageCard/src/components/EventCard.vue'
 import NotificationMessageCard from '@/components/NotificationMessageCard/src/NotificationMessageCard.vue'
 import NotificationMessageCard from '@/components/NotificationMessageCard/src/NotificationMessageCard.vue'
 
 
 const collapseVModel = ref<string[]>(['1'])
 const collapseVModel = ref<string[]>(['1'])
 
 
-const unreadCount = ref(33)
-const readCount = ref(22)
-const allCount = ref(135)
-const tabCountList = [2, 1, 99, 0, 23]
+const tabCountList = ref([0, 0, 0, 0, 0])
 const handleCount = (count: number) => {
 const handleCount = (count: number) => {
   if (!count) return 0
   if (!count) return 0
   return count > 99 ? '99+' : count
   return count > 99 ? '99+' : count
@@ -18,304 +14,132 @@ const navList = [
   'Departure/Arrival Delay',
   'Departure/Arrival Delay',
   'ETD/ETA Change'
   'ETD/ETA Change'
 ]
 ]
-const cardList = [
-  {
-    notificationType: 'event',
-    info: {
-      type: 'milestone',
-      isMultiple: true,
-      numericRecords: 3,
-      isRead: true,
-      title: 'Milestone Update',
-      mode: 'Ocean Freight',
-      no: 'HBOL: SHJN2301234',
-      tag: 'Booking Confirmed',
-      location: 'Hong Kong',
-      time: 'Jan 10, 2025 14:30 UTC+8',
-      info: {
-        route: ['Hong Kong', 'Shanghai', 'Ningbo']
-      }
-    }
-  }
-]
-
-const result = {
-  countList: [2, 1, 99, 0, 33],
-  allCount: 135,
-  unreadCount: 33,
-  readCount: 22,
-  cardList: [
-    {
-      notificationType: 'feature',
-      info: {
-        isRead: true,
-        title: 'Feature Update',
-        header: 'New feature online: Quick search has been released!',
-        content:
-          'We are pleased to announce that the quick search function is now officially online! You can now quickly find what you need by entering keywords, greatly improving your work efficiency. Go and experience it!'
-      }
-    }
-  ]
-}
 
 
 const activeItem = ref('Milestone Update')
 const activeItem = ref('Milestone Update')
+const notificationTypeList = ref({
+  Milestone_Update: 'Milestone Update',
+  Container_Status_Update: 'Container Status Update',
+  Container_Arrival: 'Container Arrival',
+  'Departure/Arrival_Delay': 'Departure/Arrival Delay',
+  'ETD/ETA_Change': 'ETD/ETA Change',
+  Feature_Update: 'Feature Update'
+})
+
 const setActiveItem = (item: string) => {
 const setActiveItem = (item: string) => {
   activeItem.value = item
   activeItem.value = item
-}
-
-// const getNotificationList = () => {
-//   $api
-//     .getNotificationList({
-//       rules_type: 'Milestone_Update'
-//     })
-//     .then((res) => {
-//       if (res.code === 200) {
-//         notificationList.value = res.data.info
-//         console.log(res, 'test')
-//       }
-//     })
-// }
+  activeName.value = 'All Notifications'
 
 
-const activeName = ref('first')
+  getNotificationList()
+}
 
 
-const handleClick = () => {}
+const loading = ref(false)
+const notificationList = ref<any[]>([])
+const unreadNotificationList = computed(() => {
+  return notificationList.value.filter((item) => !item.info.isRead)
+})
+const readNotificationList = computed(() => {
+  return notificationList.value.filter((item) => item.info.isRead)
+})
+const getNotificationList = () => {
+  loading.value = true
+  const rulesType = Object.entries(notificationTypeList.value).find(
+    (item) => item[1] === activeItem.value
+  )?.[0]
+  $api
+    .getSystemMessageData({
+      rules_type: rulesType
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        const data = res.data
+        notificationList.value = data.cardList
+        tabCountList.value = data.countList
+      }
+    })
+    .finally(() => {
+      loading.value = false
+    })
+}
 
 
-// const notificationList = ref<any[]>([])
-const notificationList = [
-  {
-    type: 'milestone',
-    isMultiple: true,
-    numericRecords: 3,
-    isRead: true,
-    title: 'Milestone Update',
-    mode: 'Ocean Freight',
-    no: 'SHJN2301234',
-    tag: 'Booking Confirmed',
-    location: 'Hong Kong',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai'
-  },
-  {
-    type: 'container',
-    isRead: false,
-    mode: '',
-    no: 'SHJN2301234',
-    tag: 'Unloaded From Vessel',
-    location: 'Hong Kong',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai',
-    previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
-  },
-  {
-    type: 'delay',
-    numericRecords: 0,
-    isRead: false,
-    title: 'Delay Daily Summary (Jan 10, 2025)',
-    mode: 'Air Freight',
-    no: 'SHJN2301234',
-    tag: 'Departure Delay',
-    location: 'Hong Kong',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai',
-    info: {
-      timeLabel: 'ATA: ',
-      time: '2510-12-1 14:30 UTC+8',
-      timezone: 'Asia/Shanghai',
-      departureDelayNum: 10,
-      arrivalDelayNum: 8
-    }
-  },
-  {
-    type: 'container',
-    isRead: false,
-    mode: '',
-    no: 'SHJN2301234',
-    tag: 'Unloaded From Vessel',
-    location: 'Hong Kong',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai',
-    previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
-  },
-  {
-    type: 'delay',
-    numericRecords: 0,
-    isRead: false,
-    title: 'Delay Daily Summary (Jan 10, 2025)',
-    mode: 'Air Freight',
-    no: 'SHJN2301234',
-    tag: 'Departure Delay',
-    location: 'Hong Kong',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai',
-    info: {
-      timeLabel: 'ATA: ',
-      time: '2510-12-1 14:30 UTC+8',
-      timezone: 'Asia/Shanghai',
-      departureDelayNum: 10,
-      arrivalDelayNum: 8
-    }
-  },
-  {
-    type: 'container',
-    isRead: false,
-    mode: '',
-    no: 'SHJN2301234',
-    tag: 'Unloaded From Vessel',
-    location: 'Hong Kong',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai',
-    previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
-  },
-  {
-    type: 'delay',
-    numericRecords: 0,
-    isRead: false,
-    title: 'Delay Daily Summary (Jan 10, 2025)',
-    mode: 'Air Freight',
-    no: 'SHJN2301234',
-    tag: 'Departure Delay',
-    location: 'Hong Kong',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai',
-    info: {
-      timeLabel: 'ATA: ',
-      time: '2510-12-1 14:30 UTC+8',
-      timezone: 'Asia/Shanghai',
-      departureDelayNum: 10,
-      arrivalDelayNum: 8
-    }
-  },
-  {
-    type: 'change',
-    numericRecords: 0,
-    isRead: false,
-    title: 'ETD/ETA  Change Weekly Summary (Jan 4- 10, 2025) ',
-    mode: 'Air Freight',
-    no: 'SHJN2301234',
-    tag: 'ETD Change',
-    info: {
-      etdChangeNum: 20,
-      etaChangeNum: 10,
-      timeLabel: 'ATA: ',
-      time: '2510-12-1 14:30 UTC+8',
-      timezone: 'Asia/Shanghai'
-    },
-    location: 'Hong Kong',
-    changeTime: 'Updated ETD: Jan 17, 15:00',
-    timeLabel: 'ATA: ',
-    time: '2510-12-1 14:30 UTC+8',
-    timezone: 'Asia/Shanghai'
-  }
-]
-// onMounted(() => {
-//   getNotificationList()
-// })
+const activeName = ref('All Notifications')
 
 
-const featureList = [
-  {
-    notificationType: 'feature',
-    info: {
-      isRead: true,
-      title: 'Feature Update',
-      header: 'New feature online: Quick search has been released!',
-      content:
-        'We are pleased to announce that the quick search function is now officially online! You can now quickly find what you need by entering keywords, greatly improving your work efficiency. Go and experience it!'
-    }
-  },
-  {
-    notificationType: 'feature',
-    info: {
-      isRead: true,
-      title: 'Feature Update',
-      header: 'New feature online: Quick search has been released!',
-      content:
-        'We are pleased to announce that the quick search function is now officially online! You can now quickly find what you need by entering keywords, greatly improving your work efficiency. Go and experience it!'
-    }
-  },
-  {
-    notificationType: 'feature',
-    info: {
-      isRead: true,
-      title: 'Feature Update',
-      header: 'New feature online: Quick search has been released!',
-      content:
-        'We are pleased to announce that the quick search function is now officially online! You can now quickly find what you need by entering keywords, greatly improving your work efficiency. Go and experience it!'
-    }
-  }
-]
+onMounted(() => {
+  getNotificationList()
+})
 </script>
 </script>
 
 
 <template>
 <template>
-  <div class="Title">System Message</div>
-  <div class="system-message">
-    <div class="left-nav">
-      <el-collapse v-model="collapseVModel">
-        <el-collapse-item title="Event Notifications" name="1">
-          <div
-            @click="setActiveItem(item)"
-            class="collapse-item"
-            :class="{ 'is-active': item === activeItem }"
-            v-for="(item, index) in navList"
-            :key="item"
-          >
-            <div v-if="item === activeItem" class="active-sign"></div>
-            <span>{{ item }}</span>
-            <div class="count">
-              <span>{{ handleCount(tabCountList?.[index]) }}</span>
+  <div v-vloading="loading" style="height: 100%; width: 100%">
+    <div class="Title">System Message</div>
+    <div class="system-message">
+      <div class="left-nav">
+        <el-collapse v-model="collapseVModel">
+          <el-collapse-item title="Event Notifications" name="1">
+            <div
+              @click="setActiveItem(item)"
+              class="collapse-item"
+              :class="{ 'is-active': item === activeItem }"
+              v-for="(item, index) in navList"
+              :key="item"
+            >
+              <div v-if="item === activeItem" class="active-sign"></div>
+              <span>{{ item }}</span>
+              <div class="count">
+                <span>{{ handleCount(tabCountList?.[index]) }}</span>
+              </div>
             </div>
             </div>
+          </el-collapse-item>
+        </el-collapse>
+        <div
+          @click="setActiveItem('Feature Update')"
+          class="collapse-item"
+          style="margin-top: 4px; font-weight: 700"
+          :class="{ 'is-active': activeItem === 'Feature Update' }"
+        >
+          <div v-if="activeItem === 'Feature Update'" class="active-sign"></div>
+          <span>Feature Update</span>
+          <div class="count">
+            <span>{{ handleCount(tabCountList?.[tabCountList.length - 1]) }}</span>
           </div>
           </div>
-        </el-collapse-item>
-      </el-collapse>
-      <div
-        @click="setActiveItem('Feature Update')"
-        class="collapse-item"
-        style="margin-top: 4px; font-weight: 700"
-        :class="{ 'is-active': activeItem === 'Feature Update' }"
-      >
-        <div v-if="activeItem === 'Feature Update'" class="active-sign"></div>
-        <span>Feature Update</span>
-        <div class="count">
-          <span>{{ handleCount(tabCountList?.[tabCountList.length - 1]) }}</span>
         </div>
         </div>
       </div>
       </div>
-    </div>
-    <div class="right-content">
-      <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
-        <el-tab-pane label="All Notifications" name="first">
-          <template #label>
-            <span style="margin-right: 4px">All Notifications</span>
-            <div class="count">
-              <span>{{ handleCount(allCount) }}</span>
+      <div class="right-content">
+        <el-tabs v-model="activeName" class="demo-tabs">
+          <el-tab-pane label="All Notifications" name="All Notifications">
+            <template #label>
+              <span style="margin-right: 4px">All Notifications</span>
+              <div class="count">
+                <span>{{ handleCount(notificationList.length) }}</span>
+              </div>
+            </template>
+            <div style="padding: 10px 140px 0px 16px">
+              <NotificationMessageCard :data="notificationList"></NotificationMessageCard>
             </div>
             </div>
-          </template>
-          <div style="padding: 10px 140px 0px 16px">
-            <NotificationMessageCard :data="cardList"></NotificationMessageCard>
-          </div>
-        </el-tab-pane>
-        <el-tab-pane label="Unread" name="second">
-          <template #label>
-            <span style="margin-right: 4px">Unread</span>
-            <div class="count">
-              <span>{{ handleCount(unreadCount) }}</span>
+          </el-tab-pane>
+          <el-tab-pane label="Unread" name="Unread">
+            <template #label>
+              <span style="margin-right: 4px">Unread</span>
+              <div class="count">
+                <span>{{ handleCount(unreadNotificationList.length) }}</span>
+              </div>
+            </template>
+            <div style="padding: 10px 140px 0px 16px">
+              <NotificationMessageCard :data="unreadNotificationList"></NotificationMessageCard>
+            </div>
+          </el-tab-pane>
+          <el-tab-pane label="Read" name="Read">
+            <template #label
+              ><span style="margin-right: 4px">Read</span>
+              <div class="count">
+                <span>{{ handleCount(readNotificationList.length) }}</span>
+              </div></template
+            >
+            <div style="padding: 10px 140px 0px 16px">
+              <NotificationMessageCard :data="readNotificationList"></NotificationMessageCard>
             </div>
             </div>
-          </template>
-        </el-tab-pane>
-        <el-tab-pane label="Read" name="third">
-          <template #label
-            ><span style="margin-right: 4px">Read</span>
-            <div class="count">
-              <span>{{ handleCount(readCount) }}</span>
-            </div></template
-          >
-        </el-tab-pane>
-      </el-tabs>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
@@ -335,11 +159,10 @@ const featureList = [
   height: calc(100% - 68px);
   height: calc(100% - 68px);
   .count {
   .count {
     display: inline-flex;
     display: inline-flex;
-    justify-content: center;
     height: 18px;
     height: 18px;
     min-width: 18px;
     min-width: 18px;
     padding-top: 1px;
     padding-top: 1px;
-    padding-left: 4px;
+    padding-left: 5px;
     padding-right: 5px;
     padding-right: 5px;
     background-color: var(--color-theme);
     background-color: var(--color-theme);
     border-radius: 9px;
     border-radius: 9px;

+ 87 - 41
src/views/SystemMessage/src/components/SystemMessageDetail.vue

@@ -1,47 +1,69 @@
 <script setup lang="ts">
 <script setup lang="ts">
+import { useRoute } from 'vue-router'
 import EventCard from '@/components/NotificationMessageCard/src/components/EventCard.vue'
 import EventCard from '@/components/NotificationMessageCard/src/components/EventCard.vue'
 
 
-const notificationData: any = {
-  title: 'Milestone Update Daily Summary (Jan 10, 2025)',
-  numericRecords: 3,
-  notificationList: [
-    {
-      mode: 'Ocean Freight',
-      no: 'HBOL: SHJN2301234',
-      tag: 'Booking Confirmed',
-      location: 'Hong Kong',
-      timeLabel: 'ATA: ',
-      time: '2510-12-1 14:30 UTC+8',
-      timezone: 'Asia/Shanghai'
-    },
-    {
-      mode: 'Air Freight',
-      no: 'HBOL: SHJN2301234',
-      tag: 'Booking Confirmed',
-      location: 'Hong Kong',
-      timeLabel: 'ATA: ',
-      time: '2510-12-1 14:30 UTC+8',
-      timezone: 'Asia/Shanghai'
-    },
-    {
-      mode: 'Air Freight',
-      no: 'HBOL: SHJN2301234',
-      tag: 'Booking Confirmed',
-      location: 'Hong Kong',
-      timeLabel: 'ATA: ',
-      time: '2510-12-1 14:30 UTC+8',
-      timezone: 'Asia/Shanghai',
-      previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
-    }
-  ]
-}
+const route = useRoute()
+const notificationData = ref({
+  title: '',
+  type: 'delay',
+  etdOrdeparturNum: -1,
+  etaOrarrivalNum: -1,
+  numericRecords: -1,
+  notificationList: []
+})
+// const notificationData: any = {
+//   title: 'Milestone Update Daily Summary (Jan 10, 2025)',
+//   numericRecords: 3,
+//   notificationList: [
+//     {
+//       mode: 'Ocean Freight',
+//       no: 'HBOL: SHJN2301234',
+//       tag: 'Booking Confirmed',
+//       location: 'Hong Kong',
+//       timeLabel: 'ATA: ',
+//       time: '2510-12-1 14:30 UTC+8',
+//       timezone: 'Asia/Shanghai'
+//     },
+//     {
+//       mode: 'Air Freight',
+//       no: 'HBOL: SHJN2301234',
+//       tag: 'Booking Confirmed',
+//       location: 'Hong Kong',
+//       timeLabel: 'ATA: ',
+//       time: '2510-12-1 14:30 UTC+8',
+//       timezone: 'Asia/Shanghai'
+//     },
+//     {
+//       mode: 'Air Freight',
+//       no: 'HBOL: SHJN2301234',
+//       tag: 'Booking Confirmed',
+//       location: 'Hong Kong',
+//       timeLabel: 'ATA: ',
+//       time: '2510-12-1 14:30 UTC+8',
+//       timezone: 'Asia/Shanghai',
+//       previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
+//     }
+//   ]
+// }
 
 
+const loading = ref(false)
 const getNotificationList = () => {
 const getNotificationList = () => {
-  $api.getNotificationDetails().then((res) => {
-    if (res.code === 200) {
-      // console.log(res, 'test')
-    }
-  })
+  loading.value = true
+  $api
+    .getNotificationDetails({
+      rules_type: route.query.rules_type,
+      frequency_type: route.query.frequency_type,
+      insert_date_format: route.query.insert_date_format
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        // console.log(res, 'test')
+        notificationData.value = res.data
+      }
+    })
+    .finally(() => {
+      loading.value = false
+    })
 }
 }
 onMounted(() => {
 onMounted(() => {
   getNotificationList()
   getNotificationList()
@@ -49,13 +71,35 @@ onMounted(() => {
 </script>
 </script>
 
 
 <template>
 <template>
-  <div class="system-message-detail">
+  <div class="system-message-detail" v-vloading="loading">
     <div class="content">
     <div class="content">
       <div class="header" v-if="notificationData.title">
       <div class="header" v-if="notificationData.title">
         <div class="status-icon"></div>
         <div class="status-icon"></div>
         <div class="title">{{ notificationData.title }}</div>
         <div class="title">{{ notificationData.title }}</div>
       </div>
       </div>
-      <div class="total-tips">Latest Status Updates ({{ notificationData.numericRecords }})</div>
+      <div class="total-tips" v-if="notificationData.numericRecords > -1">
+        Latest Status Updates ({{ notificationData.numericRecords }})
+      </div>
+      <div
+        class="total-tips"
+        v-else-if="notificationData.etdOrdeparturNum > -1 || notificationData.etaOrarrivalNum > -1"
+      >
+        <div>
+          <span v-if="notificationData.etdOrdeparturNum"
+            >{{ notificationData.type === 'delay' ? 'Departure Delay' : 'ETD Change' }} ({{
+              notificationData.etdOrdeparturNum
+            }})</span
+          >
+          <span v-if="notificationData.etdOrdeparturNum && notificationData.etaOrarrivalNum">
+            &nbsp;&nbsp;|&nbsp;&nbsp;</span
+          >
+          <span v-if="notificationData.etaOrarrivalNum">
+            {{ notificationData.type === 'delay' ? 'Arrival Delay' : 'ETA Change' }} ({{
+              notificationData.etaOrarrivalNum
+            }})
+          </span>
+        </div>
+      </div>
       <EventCard
       <EventCard
         v-for="(item, index) in notificationData.notificationList"
         v-for="(item, index) in notificationData.notificationList"
         :key="index"
         :key="index"
@@ -67,7 +111,9 @@ onMounted(() => {
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 .system-message-detail {
 .system-message-detail {
+  height: 100%;
   padding: 16px;
   padding: 16px;
+  overflow: auto;
   .content {
   .content {
     margin: auto;
     margin: auto;
     width: 800px;
     width: 800px;

+ 3 - 3
src/views/SystemSettings/src/SystemSettings.vue

@@ -2,12 +2,12 @@
 import { ref, onMounted } from 'vue'
 import { ref, onMounted } from 'vue'
 import AddRSettingTableules from './components/SettingTable'
 import AddRSettingTableules from './components/SettingTable'
 import MonitoringTable from './components/MonitoringTable'
 import MonitoringTable from './components/MonitoringTable'
-import { useRouter } from 'vue-router'
+import { useRouter, useRoute } from 'vue-router'
 import PersonalProfile from './components/PersonalProfile.vue'
 import PersonalProfile from './components/PersonalProfile.vue'
 
 
 const router = useRouter()
 const router = useRouter()
-
-const TabActive = ref('Personal Profile')
+const route = useRoute()
+const TabActive = ref(route.query.tab || 'Personal Profile')
 const isMilestoneChecked = ref(false)
 const isMilestoneChecked = ref(false)
 const isContainerChecked = ref(false)
 const isContainerChecked = ref(false)
 const isDepartureChecked = ref(false)
 const isDepartureChecked = ref(false)