Browse Source

Merge branch 'dev_zyh' of United_Software/k_online_ui into feature

Jack Zhou 9 months ago
parent
commit
32bbde45cf

+ 0 - 1
package.json

@@ -11,7 +11,6 @@
     "preview": "vite preview",
     "preview": "vite preview",
     "build-only": "vite build",
     "build-only": "vite build",
     "build:dev": "vite build --mode development",
     "build:dev": "vite build --mode development",
-    "build:test": "vite build --mode test",
     "build:pro": "vite build --mode product",
     "build:pro": "vite build --mode product",
     "type-check": "vue-tsc --build --force",
     "type-check": "vue-tsc --build --force",
     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",

+ 26 - 3
src/api/module/notificationMessage.ts

@@ -6,15 +6,38 @@ const baseUrl = `${base}/main_new_version.php`
 /**
 /**
  * 保存用户个人信息或日期和数字格式化配置
  * 保存用户个人信息或日期和数字格式化配置
  * @param save_model profile 代表基本信息的save, no_profile 代表格式信息的save
  * @param save_model profile 代表基本信息的save, no_profile 代表格式信息的save
- * @param user_name
- * @param email
+ * @param first_name
+ * @param last_name
  * @param date_fromat
  * @param date_fromat
  * @param numbers_format
  * @param numbers_format
  */
  */
 export const saveUserInfo = (params: any) => {
 export const saveUserInfo = (params: any) => {
-  return HttpAxios.get(`${baseUrl}`, {
+  return HttpAxios.post(`${baseUrl}`, {
     action: 'system_setting',
     action: 'system_setting',
     operate: 'personal_profile_save',
     operate: 'personal_profile_save',
     ...params
     ...params
   })
   })
 }
 }
+
+/**
+ * 获取milestone消息列表
+ * @param save_model profile 代表基本信息的save, no_profile 代表格式信息的save
+ */
+export const getNotificationList = (params: any) => {
+  return HttpAxios.get(`${baseUrl}`, {
+    action: 'notifications_rules',
+    operate: 'notifications_init',
+    ...params
+  })
+}
+
+/**
+ * 获取notification消息的see all详情
+ */
+export const getNotificationDetails = (params: any) => {
+  return HttpAxios.post(`${baseUrl}`, {
+    action: 'notifications_rules',
+    operate: 'notifications_see_all',
+    ...params
+  })
+}

+ 30 - 12
src/components/NotificationMessageCard/src/components/EventCard.vue

@@ -2,13 +2,14 @@
 import { transportationMode } from '@/components/transportationMode'
 import { transportationMode } from '@/components/transportationMode'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
 import { getTimezone } from '@/utils/tools'
 import { getTimezone } from '@/utils/tools'
+import dayjs from 'dayjs'
 
 
 const router = useRouter()
 const router = useRouter()
 
 
 interface EventCardPropsData {
 interface EventCardPropsData {
   type: string // 'milestone' | 'container' | 'delay' | 'change'
   type: string // 'milestone' | 'container' | 'delay' | 'change'
   numericRecords?: number // 多条记录数 (Daily Update消息)
   numericRecords?: number // 多条记录数 (Daily Update消息)
-  isRead: boolean // 是否已读 (true 已读,false 未读)
+  isRead?: boolean // 是否已读 (true 已读,false 未读)
   title?: string // Milestone Update
   title?: string // Milestone Update
   mode?: string // 运输方式
   mode?: string // 运输方式
   no: string // HBOL: SHJN2301234
   no: string // HBOL: SHJN2301234
@@ -37,8 +38,8 @@ const props = defineProps<{
 const handleSeeAll = (data: any) => {
 const handleSeeAll = (data: any) => {
   router.push({
   router.push({
     name: 'System Message Detail',
     name: 'System Message Detail',
-    params: {
-      data: JSON.stringify(data) // 将数据转换为字符串并作为查询参数传递
+    query: {
+      frequency_type: data.frequency_type
     }
     }
   })
   })
 }
 }
@@ -140,19 +141,31 @@ const handleSeeAll = (data: any) => {
         <span class="font_family icon-icon_location_b"></span>
         <span class="font_family icon-icon_location_b"></span>
         <span>{{ data.location }}</span>
         <span>{{ data.location }}</span>
       </div>
       </div>
-      <div class="delay-time" v-if="data.type === 'delay' && data.info?.time">
-        <span class="font_family icon-icon_delay_b"></span>
-        <span>{{ data.info.timeLabel }}</span>
-        <span>{{ data.info.time }}</span>
+      <div
+        :class="{ 'delay-time': data.type === 'delay', 'change-time': data.type === 'change' }"
+        v-if="(data.type === 'delay' || 'change') && data.info?.time"
+      >
+        <span
+          v-if="data.type === 'delay'"
+          style="margin-right: 5px"
+          class="font_family icon-icon_delay_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: 3px">{{
+          dayjs(data.info.time).format('MMM DD, YYYY hh:mm')
+        }}</span>
         <span>{{ getTimezone(data.info.timezone) }}</span>
         <span>{{ getTimezone(data.info.timezone) }}</span>
       </div>
       </div>
-      <div class="change-time" v-if="data.type === 'change' && data.info?.time">
+      <!-- <div class="change-time" v-if="data.type === 'change' && data.info?.time">
         <span style="margin-left: 1px" class="font_family icon-icon_time_b"></span>
         <span style="margin-left: 1px" class="font_family icon-icon_time_b"></span>
         <span>{{ data.info?.time }}</span>
         <span>{{ data.info?.time }}</span>
-      </div>
-      <div class="time">
-        <span style="margin-left: 1px" class="font_family icon-icon_time_b"></span>
-        <span>{{ data.time }}</span>
+      </div> -->
+      <div class="time" :class="{ grey: data.type === 'delay' || data.type === 'change' }">
+        <span class="font_family icon-icon_time_b"></span>
+        <span style="margin-right: 2px">{{ data.timeLabel }}</span>
+        <span style="margin-right: 3px">{{ dayjs(data.time).format('MMM DD, YYYY hh:mm') }}</span>
+        <span>{{ getTimezone(data.timezone) }}</span>
       </div>
       </div>
       <div class="previous" v-if="data.previous">
       <div class="previous" v-if="data.previous">
         <span class="previous-icon"></span>
         <span class="previous-icon"></span>
@@ -272,6 +285,11 @@ const handleSeeAll = (data: any) => {
       align-items: center;
       align-items: center;
       margin-top: 8px;
       margin-top: 8px;
       height: 16px;
       height: 16px;
+      &.grey {
+        span {
+          color: var(--color-neutral-3);
+        }
+      }
       span {
       span {
         color: var(--color-neutral-2);
         color: var(--color-neutral-2);
         font-size: 12px;
         font-size: 12px;

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

@@ -1,5 +1,6 @@
 import { defineStore } from 'pinia'
 import { defineStore } from 'pinia'
 import { useVisitedRowState } from './visitedRow'
 import { useVisitedRowState } from './visitedRow'
+import dayjs from 'dayjs'
 
 
 interface UserInfo {
 interface UserInfo {
   uname: string
   uname: string
@@ -10,6 +11,8 @@ interface UserInfo {
   expire_day: number
   expire_day: number
   date_format: string
   date_format: string
   numbers_format: string
   numbers_format: string
+  PASSWORD_CHANGE_CYCLE: number // 密码修改周期(多少天需要改一次)
+  last_pwd_change: string // 上次密码修改时间
 }
 }
 interface UserState {
 interface UserState {
   userInfo: UserInfo
   userInfo: UserInfo
@@ -29,6 +32,11 @@ export const useUserStore = defineStore('user', {
       } else {
       } else {
         return state.userInfo.uname || ''
         return state.userInfo.uname || ''
       }
       }
+    },
+    expireDay(state) {
+      const userInfo = state.userInfo
+
+      return userInfo.PASSWORD_CHANGE_CYCLE - dayjs().diff(dayjs(userInfo.last_pwd_change), 'day')
     }
     }
   },
   },
   actions: {
   actions: {

+ 1 - 1
src/utils/tools.ts

@@ -26,7 +26,7 @@ export const formatTimezone = (time: string, timezone?: string) => {
  * @returns
  * @returns
  */
  */
 export const getTimezone = (timezone: string): string => {
 export const getTimezone = (timezone: string): string => {
-  if (timezone) return ''
+  if (!timezone) return ''
   const offset = moment.tz(`${moment().year()}-01-01`, timezone).format('Z')
   const offset = moment.tz(`${moment().year()}-01-01`, timezone).format('Z')
   return `UTC${offset.slice(0, 3)}`
   return `UTC${offset.slice(0, 3)}`
 }
 }

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

@@ -206,7 +206,7 @@ const notificationDrawer = ref(false)
           class="font_family icon-icon_notice_b"
           class="font_family icon-icon_notice_b"
           style="font-size: 18px"
           style="font-size: 18px"
         ></span> -->
         ></span> -->
-
+        <span class="unread-tip-icon"></span>
         <el-button
         <el-button
           style="height: 40px; width: 40px; margin-right: 0px"
           style="height: 40px; width: 40px; margin-right: 0px"
           class="el-button--text"
           class="el-button--text"
@@ -405,11 +405,21 @@ const notificationDrawer = ref(false)
   }
   }
 
 
   .notice-icon {
   .notice-icon {
+    position: relative;
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: center;
     justify-content: center;
     margin-top: 2px;
     margin-top: 2px;
     cursor: pointer;
     cursor: pointer;
+    .unread-tip-icon {
+      position: absolute;
+      top: 9px;
+      right: 8px;
+      width: 5px;
+      height: 5px;
+      border-radius: 50%;
+      background-color: var(--color-theme);
+    }
   }
   }
 }
 }
 </style>
 </style>

+ 9 - 0
src/views/Layout/src/components/Header/components/ChangePasswordDialog.vue

@@ -1,4 +1,9 @@
 <script setup lang="ts">
 <script setup lang="ts">
+import { useUserStore } from '@/stores/modules/user'
+import dayjs from 'dayjs'
+
+const userStore = useUserStore()
+
 const dialogVisible = ref(false)
 const dialogVisible = ref(false)
 
 
 const openDialog = () => {
 const openDialog = () => {
@@ -50,6 +55,10 @@ const handleUpdate = () => {
     .then((res) => {
     .then((res) => {
       if (res.code === 200) {
       if (res.code === 200) {
         ElMessage.success('Password updated successfully')
         ElMessage.success('Password updated successfully')
+        userStore.setUserInfo({
+          ...userStore.userInfo,
+          last_pwd_change: dayjs().format('YYYY-MM-DD HH:mm:ss')
+        })
         dialogVisible.value = false
         dialogVisible.value = false
       } else if (res.code === 400) {
       } else if (res.code === 400) {
         if (res.msg === 'Old password is incorrect') {
         if (res.msg === 'Old password is incorrect') {

+ 152 - 103
src/views/Layout/src/components/Header/components/NotificationDrawer.vue

@@ -1,126 +1,172 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import NotificationMessageCard from '@/components/NotificationMessageCard/src/NotificationMessageCard.vue'
 import NotificationMessageCard from '@/components/NotificationMessageCard/src/NotificationMessageCard.vue'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+const loading = ref(false)
 
 
 const drawerModel = defineModel('drawerModel', { type: Boolean, default: false })
 const drawerModel = defineModel('drawerModel', { type: Boolean, default: false })
+
 const notificationType = ref('all')
 const notificationType = ref('all')
-const notificationTypeList = ref([
-  {
-    label: 'All Notifications',
-    value: 'all'
-  },
-  {
-    label: 'Milestone Update',
-    value: 'feature1'
-  },
-  {
-    label: 'Container Status Update',
-    value: 'all2'
-  },
-  {
-    label: 'Departure/Arrival Delay',
-    value: 'feature3'
-  },
-  {
-    label: 'ETD/ETA Change',
-    value: 'all4'
-  },
-  {
-    label: 'Feature Update',
-    value: 'feature5'
-  }
-])
+const notificationTypeList = ref({
+  all: 'All Notifications',
+  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 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']
+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 = () => {
+  loading.value = true
+  $api
+    .getNotificationList({
+      rules_type: 'Milestone_Update'
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        notificationList.value = res.data
+        console.log(res, 'test')
       }
       }
-    }
-  },
-  {
-    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.'
-    }
-  }
-]
+    })
+    .finally(() => {
+      loading.value = false
+    })
+}
+
+const handleMarkAllRead = () => {
+  // 标记所有为已读
+  // notificationList.value.forEach((item) => {
+  //   item.isRead = true
+  // })
+}
+
+const handleViewAll = () => {
+  router.push('/system-message')
+}
+
+const handleSettingMessage = () => {
+  // 跳转消息设置页面
+  // router.push('/')
+}
+
+const clearData = () => {
+  notificationList.value = []
+}
 </script>
 </script>
 
 
 <template>
 <template>
-  <el-drawer class="notice-drawer" v-model="drawerModel" size="432px">
+  <el-drawer
+    @open="getNotificationList"
+    @closed="clearData"
+    class="notice-drawer"
+    v-model="drawerModel"
+    size="432px"
+  >
     <template #header>
     <template #header>
       <el-select size="large" v-model="notificationType" class="notification-type">
       <el-select size="large" v-model="notificationType" class="notification-type">
         <el-option
         <el-option
-          v-for="item in notificationTypeList"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
+          v-for="(label, value) in notificationTypeList"
+          :key="value"
+          :label="label"
+          :value="value"
         />
         />
       </el-select>
       </el-select>
     </template>
     </template>
     <template #default>
     <template #default>
-      <div class="notification-header">
-        <div class="mark-all-read">
-          <span class="font_family icon-icon_confirm_b show-icon"></span>
-          <span>Mark all read</span>
+      <div v-vloading="loading" style="height: 100%">
+        <div class="notification-header">
+          <el-button @click="handleMarkAllRead" class="mark-all-read el-button--text" size="small">
+            <span class="font_family icon-icon_confirm_b show-icon"></span>
+            <span>Mark all read</span>
+          </el-button>
+          <div class="view-all">
+            <el-button
+              style="height: 24px; width: 86px"
+              class="el-button--text"
+              @click="handleViewAll"
+            >
+              <span class="font_family icon-icon_confirm_b show-icon"></span>
+              <span>View all</span>
+            </el-button>
+            <el-button
+              class="el-button--text"
+              style="height: 24px; width: 24px; padding: 0"
+              @click="handleSettingMessage"
+            >
+              <span class="font_family icon-icon_administration_b"></span>
+            </el-button>
+          </div>
         </div>
         </div>
-        <div class="view-all">
-          <span class="font_family icon-icon_confirm_b show-icon"></span>
-          <span>View all</span>
-          <span class="font_family icon-icon_administration_b setting"></span>
+        <div class="notification-content">
+          <NotificationMessageCard :data="notificationList" />
         </div>
         </div>
       </div>
       </div>
-      <div class="notification-content">
-        <NotificationMessageCard :data="notificationList" />
-      </div>
     </template>
     </template>
   </el-drawer>
   </el-drawer>
 </template>
 </template>
@@ -235,6 +281,7 @@ div.layout-toolbar {
     .notification-header {
     .notification-header {
       display: flex;
       display: flex;
       justify-content: space-between;
       justify-content: space-between;
+      align-items: center;
       position: sticky;
       position: sticky;
       top: 0;
       top: 0;
       height: 40px;
       height: 40px;
@@ -245,9 +292,11 @@ div.layout-toolbar {
       .mark-all-read {
       .mark-all-read {
         span {
         span {
           color: var(--color-theme);
           color: var(--color-theme);
+          font-size: 14px;
           vertical-align: middle;
           vertical-align: middle;
         }
         }
         .show-icon {
         .show-icon {
+          font-size: 16px;
           margin-right: 2px;
           margin-right: 2px;
         }
         }
       }
       }

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

@@ -17,49 +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: 'Operation Log',
-          path: '/Operationlog'
-        },
-        {
-          index: '4-2',
-          label: 'System Message',
-          path: '/system-message'
-        }
-      ]
+  $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()
 //监听窗口大小
 //监听窗口大小

+ 100 - 9
src/views/SystemMessage/src/SystemMessage.vue

@@ -1,6 +1,5 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import EventCard from '@/components/NotificationMessageCard/src/components/EventCard.vue'
 import EventCard from '@/components/NotificationMessageCard/src/components/EventCard.vue'
-import PersonalProfile from './components/PersonalProfile.vue'
 
 
 const collapseVModel = ref<string[]>(['1'])
 const collapseVModel = ref<string[]>(['1'])
 
 
@@ -28,10 +27,24 @@ 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')
+//       }
+//     })
+// }
+
 const activeName = ref('first')
 const activeName = ref('first')
 
 
 const handleClick = () => {}
 const handleClick = () => {}
 
 
+// const notificationList = ref<any[]>([])
 const notificationList = [
 const notificationList = [
   {
   {
     type: 'milestone',
     type: 'milestone',
@@ -43,7 +56,73 @@ const notificationList = [
     no: 'SHJN2301234',
     no: 'SHJN2301234',
     tag: 'Booking Confirmed',
     tag: 'Booking Confirmed',
     location: 'Hong Kong',
     location: 'Hong Kong',
-    time: 'Jan 10, 2025 14:30 UTC+8'
+    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',
     type: 'container',
@@ -52,7 +131,9 @@ const notificationList = [
     no: 'SHJN2301234',
     no: 'SHJN2301234',
     tag: 'Unloaded From Vessel',
     tag: 'Unloaded From Vessel',
     location: 'Hong Kong',
     location: 'Hong Kong',
-    time: 'Jan 10, 2025 14:30 UTC+8',
+    timeLabel: 'ATA: ',
+    time: '2510-12-1 14:30 UTC+8',
+    timezone: 'Asia/Shanghai',
     previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
     previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
   },
   },
   {
   {
@@ -64,9 +145,13 @@ const notificationList = [
     no: 'SHJN2301234',
     no: 'SHJN2301234',
     tag: 'Departure Delay',
     tag: 'Departure Delay',
     location: 'Hong Kong',
     location: 'Hong Kong',
-    time: 'Jan 10, 2025 14:30 UTC+8',
+    timeLabel: 'ATA: ',
+    time: '2510-12-1 14:30 UTC+8',
+    timezone: 'Asia/Shanghai',
     info: {
     info: {
-      time: 'ATD: Jan 12, 16:30 (+2 days delay)',
+      timeLabel: 'ATA: ',
+      time: '2510-12-1 14:30 UTC+8',
+      timezone: 'Asia/Shanghai',
       departureDelayNum: 10,
       departureDelayNum: 10,
       arrivalDelayNum: 8
       arrivalDelayNum: 8
     }
     }
@@ -82,13 +167,20 @@ const notificationList = [
     info: {
     info: {
       etdChangeNum: 20,
       etdChangeNum: 20,
       etaChangeNum: 10,
       etaChangeNum: 10,
-      time: 'Updated ETD: Jan 17, 15:00'
+      timeLabel: 'ATA: ',
+      time: '2510-12-1 14:30 UTC+8',
+      timezone: 'Asia/Shanghai'
     },
     },
     location: 'Hong Kong',
     location: 'Hong Kong',
     changeTime: 'Updated ETD: Jan 17, 15:00',
     changeTime: 'Updated ETD: Jan 17, 15:00',
-    time: 'Jan 10, 2025 14:30 UTC+8'
+    timeLabel: 'ATA: ',
+    time: '2510-12-1 14:30 UTC+8',
+    timezone: 'Asia/Shanghai'
   }
   }
 ]
 ]
+// onMounted(() => {
+//   getNotificationList()
+// })
 </script>
 </script>
 
 
 <template>
 <template>
@@ -143,7 +235,6 @@ const notificationList = [
               <span>33</span>
               <span>33</span>
             </div>
             </div>
           </template>
           </template>
-          <PersonalProfile />
         </el-tab-pane>
         </el-tab-pane>
         <el-tab-pane label="Read" name="third">Role</el-tab-pane>
         <el-tab-pane label="Read" name="third">Role</el-tab-pane>
       </el-tabs>
       </el-tabs>
@@ -250,7 +341,7 @@ const notificationList = [
     }
     }
   }
   }
   :deep(.el-tabs) {
   :deep(.el-tabs) {
-    height: calc(100% - 40px);
+    height: calc(100%);
     .el-tabs__content {
     .el-tabs__content {
       overflow-y: auto;
       overflow-y: auto;
     }
     }

+ 20 - 6
src/views/SystemMessage/src/components/SystemMessageDetail.vue

@@ -6,32 +6,46 @@ const notificationData: any = {
   numericRecords: 3,
   numericRecords: 3,
   notificationList: [
   notificationList: [
     {
     {
-      isRead: true,
       mode: 'Ocean Freight',
       mode: 'Ocean Freight',
       no: 'HBOL: SHJN2301234',
       no: 'HBOL: SHJN2301234',
       tag: 'Booking Confirmed',
       tag: 'Booking Confirmed',
       location: 'Hong Kong',
       location: 'Hong Kong',
-      time: 'Jan 10, 2025 14:30 UTC+8'
+      timeLabel: 'ATA: ',
+      time: '2510-12-1 14:30 UTC+8',
+      timezone: 'Asia/Shanghai'
     },
     },
     {
     {
-      isRead: false,
       mode: 'Air Freight',
       mode: 'Air Freight',
       no: 'HBOL: SHJN2301234',
       no: 'HBOL: SHJN2301234',
       tag: 'Booking Confirmed',
       tag: 'Booking Confirmed',
       location: 'Hong Kong',
       location: 'Hong Kong',
-      time: 'Jan 10, 2025 14:30 UTC+8'
+      timeLabel: 'ATA: ',
+      time: '2510-12-1 14:30 UTC+8',
+      timezone: 'Asia/Shanghai'
     },
     },
     {
     {
-      isRead: false,
       mode: 'Air Freight',
       mode: 'Air Freight',
       no: 'HBOL: SHJN2301234',
       no: 'HBOL: SHJN2301234',
       tag: 'Booking Confirmed',
       tag: 'Booking Confirmed',
       location: 'Hong Kong',
       location: 'Hong Kong',
-      time: 'Jan 10, 2025 14:30 UTC+8',
+      timeLabel: 'ATA: ',
+      time: '2510-12-1 14:30 UTC+8',
+      timezone: 'Asia/Shanghai',
       previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
       previous: 'Previous: Departure from Shanghai (08:15 UTC+8)'
     }
     }
   ]
   ]
 }
 }
+
+const getNotificationList = () => {
+  $api.getNotificationDetails().then((res) => {
+    if (res.code === 200) {
+      console.log(res, 'test')
+    }
+  })
+}
+onMounted(() => {
+  getNotificationList()
+})
 </script>
 </script>
 
 
 <template>
 <template>

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

@@ -3,10 +3,11 @@ 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 } from 'vue-router'
+import PersonalProfile from './components/PersonalProfile.vue'
 
 
 const router = useRouter()
 const router = useRouter()
 
 
-const TabActive = ref('Subscribe Notifications')
+const TabActive = ref('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)
@@ -212,7 +213,7 @@ onMounted(() => {
 <template>
 <template>
   <div class="Title">System Settings</div>
   <div class="Title">System Settings</div>
   <el-tabs v-model="TabActive" class="demo-tabs">
   <el-tabs v-model="TabActive" class="demo-tabs">
-    <el-tab-pane label="Personal Profile" name="Personal Profile"> Personal Profile </el-tab-pane>
+    <el-tab-pane label="Personal Profile" name="Personal Profile"><PersonalProfile /></el-tab-pane>
     <el-tab-pane label="Subscribe Notifications" name="Subscribe Notifications">
     <el-tab-pane label="Subscribe Notifications" name="Subscribe Notifications">
       <div class="subscribedTitle">Notification Events for Subscribed Shipments</div>
       <div class="subscribedTitle">Notification Events for Subscribed Shipments</div>
       <div class="SubscribeCollapse">
       <div class="SubscribeCollapse">
@@ -455,4 +456,11 @@ onMounted(() => {
 .el-button--main {
 .el-button--main {
   margin-bottom: 4px;
   margin-bottom: 4px;
 }
 }
-</style>
+
+.demo-tabs {
+  height: calc(100% - 68px);
+  :deep(.el-tabs__content) {
+    overflow-y: auto;
+  }
+}
+</style>

+ 37 - 13
src/views/SystemMessage/src/components/PersonalProfile.vue → src/views/SystemSettings/src/components/PersonalProfile.vue

@@ -76,15 +76,13 @@ const handleDateFormat = (value: string) => {
   monthFormat.value = dateFormatExample.value[value][0].value
   monthFormat.value = dateFormatExample.value[value][0].value
 }
 }
 
 
-const numbersFormat = ref(userStore.userInfo?.numbers_format)
-
-// 判断用户是否指定数字格式化,没有指定则自动判断
-const isSpecifyNumbersFormat = () => {
-  numbersFormat.value = isEuropean() ? 'European' : 'US/UK'
+const initNumbersFormat = () => {
+  return userStore.userInfo?.numbers_format || (isEuropean() ? 'European' : 'US/UK')
 }
 }
-isSpecifyNumbersFormat()
+const numbersFormat = ref(initNumbersFormat())
 
 
 const saveConfig = (model: string) => {
 const saveConfig = (model: string) => {
+  console.log(123123)
   let params = {}
   let params = {}
   if (model === 'profile') {
   if (model === 'profile') {
     params = {
     params = {
@@ -95,13 +93,31 @@ const saveConfig = (model: string) => {
   } else {
   } else {
     params = {
     params = {
       save_model: 'no_profile',
       save_model: 'no_profile',
-      date_fromat: monthFormat.value,
-      number_format: numbersFormat.value
+      date_format: monthFormat.value,
+      numbers_format: numbersFormat.value
     }
     }
   }
   }
+  console.log(params, 'value')
   $api.saveUserInfo(params).then((res: any) => {
   $api.saveUserInfo(params).then((res: any) => {
-    if (res.cdoe === 200) {
-      console.log(res)
+    console.log(res, 'res')
+    if (res.code === 200) {
+      const updatedInfo =
+        model === 'profile'
+          ? {
+              ...userStore.userInfo,
+              first_name: form.firstName,
+              last_name: form.lastName
+            }
+          : {
+              ...userStore.userInfo,
+              date_format: monthFormat.value,
+              numbers_format: numbersFormat.value
+            }
+
+      userStore.setUserInfo(updatedInfo)
+      ElMessage.success('Save successfully')
+    } else {
+      ElMessage.error('Save failed')
     }
     }
   })
   })
 }
 }
@@ -138,7 +154,9 @@ const saveConfig = (model: string) => {
               Password
               Password
               <span style="margin: 0 2px 0 8px" class="font_family icon-icon_time_b"></span>
               <span style="margin: 0 2px 0 8px" class="font_family icon-icon_time_b"></span>
               <span>Your password will be expire in</span>
               <span>Your password will be expire in</span>
-              <span style="margin-left: 4px; color: var(--color-theme)">4 day(s)</span>
+              <span style="margin-left: 4px; color: var(--color-theme)"
+                >{{ userStore.expireDay }} day(s)</span
+              >
             </p>
             </p>
             <div class="password-change">
             <div class="password-change">
               <el-input
               <el-input
@@ -213,7 +231,7 @@ const saveConfig = (model: string) => {
                   style="padding: 0 40px"
                   style="padding: 0 40px"
                   class="el-button--dark save-icon"
                   class="el-button--dark save-icon"
                   size="large"
                   size="large"
-                  >Save</el-button
+                  >Save1222</el-button
                 >
                 >
               </el-col>
               </el-col>
             </el-row>
             </el-row>
@@ -240,7 +258,11 @@ const saveConfig = (model: string) => {
                 </el-radio></el-col
                 </el-radio></el-col
               >
               >
               <el-col>
               <el-col>
-                <el-button style="padding: 0 40px" class="el-button--dark save-icon" size="large"
+                <el-button
+                  @click="saveConfig('no_profile')"
+                  style="padding: 0 40px"
+                  class="el-button--dark save-icon"
+                  size="large"
                   >Save</el-button
                   >Save</el-button
                 >
                 >
               </el-col>
               </el-col>
@@ -256,6 +278,8 @@ const saveConfig = (model: string) => {
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
 .personal-profile {
 .personal-profile {
+  padding: 24px;
+  padding-top: 9px;
   .basic-information,
   .basic-information,
   .personal-preferences {
   .personal-preferences {
     border: 1px solid var(--color-border);
     border: 1px solid var(--color-border);