Răsfoiți Sursa

Merge branch 'feat_system_g' of United_Software/k_online_ui into feature

Jack Zhou 9 luni în urmă
părinte
comite
81279a2ca8
63 a modificat fișierele cu 6796 adăugiri și 18 ștergeri
  1. 3 1
      src/api/index.ts
  2. 146 0
      src/api/module/system.ts
  3. 1 2
      src/auto-imports.d.ts
  4. 1 0
      src/components/AddRules/index.ts
  5. 991 0
      src/components/AddRules/src/AddRules.vue
  6. 78 0
      src/components/AddRules/src/components/AddedrluesTag.vue
  7. 279 0
      src/components/AddRules/src/components/DelayedType.vue
  8. 341 0
      src/components/AddRules/src/components/ETDShipments.vue
  9. 398 0
      src/components/AddRules/src/components/NotiFrequency.vue
  10. 92 0
      src/components/AddRules/src/components/NotiMethods.vue
  11. 83 0
      src/components/AddRules/src/components/RulesShipments.vue
  12. 196 0
      src/components/AddRules/src/components/ShipmentRange.vue
  13. BIN
      src/components/AddRules/src/images/icon_collapse.png
  14. BIN
      src/components/AddRules/src/images/icon_expand.png
  15. BIN
      src/components/AddRules/src/images/illustration_email@2x.png
  16. BIN
      src/components/AddRules/src/images/illustration_system massage@2x.png
  17. BIN
      src/components/AddRules/src/images/submit_successful.png
  18. 1 0
      src/components/CreateAddRules/index.ts
  19. 1215 0
      src/components/CreateAddRules/src/CreateAddRules.vue
  20. 78 0
      src/components/CreateAddRules/src/components/AddedrluesTag.vue
  21. 279 0
      src/components/CreateAddRules/src/components/DelayedType.vue
  22. 341 0
      src/components/CreateAddRules/src/components/ETDShipments.vue
  23. 398 0
      src/components/CreateAddRules/src/components/NotiFrequency.vue
  24. 92 0
      src/components/CreateAddRules/src/components/NotiMethods.vue
  25. 83 0
      src/components/CreateAddRules/src/components/RulesShipments.vue
  26. 197 0
      src/components/CreateAddRules/src/components/ShipmentRange.vue
  27. BIN
      src/components/CreateAddRules/src/images/icon_collapse.png
  28. BIN
      src/components/CreateAddRules/src/images/icon_expand.png
  29. BIN
      src/components/CreateAddRules/src/images/illustration_email@2x.png
  30. BIN
      src/components/CreateAddRules/src/images/illustration_system massage@2x.png
  31. BIN
      src/components/CreateAddRules/src/images/submit_successful.png
  32. 46 0
      src/components/TableEmpty/TableEmpty.vue
  33. BIN
      src/components/TableEmpty/image/default_notification_setting.png
  34. 1 0
      src/components/TableEmpty/index.ts
  35. 13 0
      src/router/index.ts
  36. 1 1
      src/stores/modules/breadCrumb.ts
  37. 16 0
      src/styles/elementui.scss
  38. 32 4
      src/styles/icons/iconfont.css
  39. 0 0
      src/styles/icons/iconfont.js
  40. 14 0
      src/styles/icons/iconfont.svg
  41. BIN
      src/styles/icons/iconfont.ttf
  42. BIN
      src/styles/icons/iconfont.woff
  43. BIN
      src/styles/icons/iconfont.woff2
  44. 4 0
      src/styles/theme.scss
  45. 27 0
      src/utils/timezone.ts
  46. 21 6
      src/views/Dashboard/src/components/RecentStatus.vue
  47. 1 0
      src/views/Layout/src/components/Header/components/LogoutDialog.vue
  48. 1 0
      src/views/Login/src/loginView.vue
  49. 2 2
      src/views/OperationLog/src/components/BookingTable/src/BookingTable.vue
  50. 1 0
      src/views/SystemSettings/index.ts
  51. 458 0
      src/views/SystemSettings/src/SystemSettings.vue
  52. 1 0
      src/views/SystemSettings/src/components/CreateNewrule/index.ts
  53. 275 0
      src/views/SystemSettings/src/components/CreateNewrule/src/CreateNewrule.vue
  54. 1 0
      src/views/SystemSettings/src/components/MonitoringTable/index.ts
  55. 261 0
      src/views/SystemSettings/src/components/MonitoringTable/src/MonitoringTable.vue
  56. 1 0
      src/views/SystemSettings/src/components/SettingTable/index.ts
  57. 212 0
      src/views/SystemSettings/src/components/SettingTable/src/SettingTable.vue
  58. BIN
      src/views/SystemSettings/src/images/icon_collapse_colorful.png
  59. BIN
      src/views/SystemSettings/src/images/icon_expand_colorful.png
  60. BIN
      src/views/SystemSettings/src/images/xia.png
  61. 57 0
      src/views/Tracking/src/components/TrackingDetail/src/TrackingDetail.vue
  62. 56 2
      src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue
  63. 1 0
      vite.config.ts

+ 3 - 1
src/api/index.ts

@@ -3,6 +3,7 @@ import * as tracking from './module/tracking'
 import * as common from './module/common'
 import * as login from './module/login'
 import * as other from './module/other'
+import * as system from './module/system'
 /**
  * api 对象接口定义
  */
@@ -19,7 +20,8 @@ const apis = generateApiMap({
   ...tracking,
   ...common,
   ...login,
-  ...other
+  ...other,
+  ...system
 })
 export default {
   ...apis // 取出所有可遍历属性赋值在新的对象上

+ 146 - 0
src/api/module/system.ts

@@ -0,0 +1,146 @@
+import HttpAxios from '@/utils/axios'
+
+const base = import.meta.env.VITE_API_HOST
+const baseUrl = `${base}/main_new_version.php`
+
+
+/**
+ * 获取subscribe数据
+ */
+export const getsubscribe = (params: any, config: any) => {
+  return HttpAxios.get(
+    `${baseUrl}`,
+    {
+      action: 'system_setting',
+      operate: 'subscribe_notification_init',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 提交subscribe数据
+ */
+export const Savesubscribe = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'system_setting',
+      operate: 'subscribe_notification_event_update',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 删除AddedRules表格数据
+ */
+export const DeleteAddedRules = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'system_setting',
+      operate: 'subscribe_notification_rules_delete',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 收藏
+ */
+export const SubscribeShipments = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'system_setting',
+      operate: 'subscribe_shipment',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 收藏表格分页切换
+ */
+export const SubscribePagination = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'system_setting',
+      operate: 'subscribe_shipment_search',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 初始化数据
+ */
+export const MonitoringInit = (params: any, config: any) => {
+  return HttpAxios.get(
+    `${baseUrl}`,
+    {
+      action: 'monitoring_setting',
+      operate: 'monitoring_rules_init',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * Monitoring保存数据
+ */
+export const MonitoringSave = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'monitoring_setting',
+      operate: 'monitoring_rules_do',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * Monitoring表格数据
+ */
+export const MonitoringTable = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'monitoring_setting',
+      operate: 'monitoring_rules_search',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 删除Monitoring表格数据
+ */
+export const deleteMonitoringTable = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'monitoring_setting',
+      operate: 'monitoring_rules_delete',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 编辑Monitoring表格数据
+ */
+export const EditMonitoringTable = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'monitoring_setting',
+      operate: 'monitoring_rules_edit',
+      ...params
+    },
+    config
+  )
+}

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

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

+ 1 - 0
src/components/AddRules/index.ts

@@ -0,0 +1 @@
+export { default } from './src/AddRules.vue'

+ 991 - 0
src/components/AddRules/src/AddRules.vue

@@ -0,0 +1,991 @@
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+import RulesShipments from './components/RulesShipments.vue'
+import AddedrluesTag from './components/AddedrluesTag.vue'
+import DelayedType from './components/DelayedType.vue'
+import ETDShipments from './components/ETDShipments.vue'
+import NotiFrequency from './components/NotiFrequency.vue'
+import NotiMethods from './components/NotiMethods.vue'
+import submitsucessful from './images/submit_successful.png'
+interface CheckboxItem {
+  value: string
+  label: string
+}
+
+interface Props {
+  SystemList: Object
+  TitleType: String
+}
+const MilestoneOceanListInit = ref<CheckboxItem[]>([])
+const MilestoneOceanListChecked = ref()
+const MilestoneAirListInit = ref<CheckboxItem[]>([])
+const MilestoneAirListChecked = ref()
+const ContainerOceanListInit = ref<CheckboxItem[]>([])
+const ContainerOceanListChecked = ref()
+const props = defineProps<Props>()
+let savesubscribeobj: any = {}
+const SystemList = ref(props.SystemList)
+const RulesActive = ref(['SelectMilestone', 'NotificationFrequency', 'NotificationMethod'])
+const OceanCheckList = ref()
+const AirCheckList = ref()
+const ContainerOceanList = ref()
+const IsFirstActive = ref(true)
+const IsTwoActive = ref(true)
+const IsThreeActive = ref(true)
+const CancelRulesVisible = ref(false)
+const UnableSaveVisible = ref(false)
+const SaveedVisible = ref(false)
+const DelayedDeparturedList = ref()
+const DelayedAirdList = ref()
+const ETDOceanList = ref()
+const ETDAirList = ref()
+const FrequencyDataMil = ref()
+const FrequencyDataCon = ref()
+const FrequencyDataDep = ref()
+const FrequencyDataETD = ref()
+const MethodsDataMil = ref()
+const MethodsDataCon = ref()
+const MethodsDataDep = ref()
+const MethodsDataETD = ref()
+const DelayedDataInit = ref()
+const DelayedDataInitAir = ref()
+const OceanETDInit = ref()
+const AirETDInit = ref()
+
+watch(
+  () => props.SystemList,
+  (current) => {
+    SystemList.value = current
+    FrequencyDataMil.value = current['Milestone_Update']
+    MethodsDataMil.value = current['Milestone_Update']
+    FrequencyDataCon.value = current['Container_Status_Update']
+    MethodsDataCon.value = current['Container_Status_Update']
+    FrequencyDataDep.value = current['Departure/Arrival_Delay']
+    MethodsDataDep.value = current['Departure/Arrival_Delay']
+    FrequencyDataETD.value = current['ETD/ETA_Change']
+    MethodsDataETD.value = current['ETD/ETA_Change']
+    Initdata(current)
+  }
+)
+
+// 初始赋值
+const Initdata = (val: any) => {
+  MilestoneOceanListInit.value = val.Milestone_Update.OceanCheckBoxList
+  MilestoneOceanListChecked.value = val.Milestone_Update.OceanCheckedList
+  OceanCheckList.value = val.Milestone_Update.OceanCheckedList
+  MilestoneAirListInit.value = val.Milestone_Update.AirCheckBoxList
+  MilestoneAirListChecked.value = val.Milestone_Update.AirCheckedList
+  AirCheckList.value = val.Milestone_Update.AirCheckedList
+  ContainerOceanListInit.value = val.Container_Status_Update.CtnrCheckBoxList
+  ContainerOceanListChecked.value = val.Container_Status_Update.CtnrCheckedList
+  ContainerOceanList.value = val.Container_Status_Update.CtnrCheckedList
+  let OceanObj: any = {}
+  OceanObj.atd_etd = val['Departure/Arrival_Delay'].ocean_atd_sub_etd
+  OceanObj.atd_etd_unit = val['Departure/Arrival_Delay'].ocean_atd_sub_etd_unit
+  OceanObj.ata_eta = val['Departure/Arrival_Delay'].ocean_ata_sub_eta
+  OceanObj.ata_eta_unit = val['Departure/Arrival_Delay'].ocean_ata_sub_eta_unit
+  DelayedDataInit.value = OceanObj
+  let AirObj: any = {}
+  AirObj.atd_etd = val['Departure/Arrival_Delay'].air_atd_sub_etd
+  AirObj.atd_etd_unit = val['Departure/Arrival_Delay'].air_atd_sub_etd_unit
+  AirObj.ata_eta = val['Departure/Arrival_Delay'].air_ata_sub_eta
+  AirObj.ata_eta_unit = val['Departure/Arrival_Delay'].air_ata_sub_eta_unit
+  DelayedDataInitAir.value = AirObj
+  let OceanChange: any = {}
+  OceanChange.ETDradio = val['ETD/ETA_Change'].ocean_etd_change
+  OceanChange.etd_old_sub_new = val['ETD/ETA_Change'].ocean_etd_old_sub_new
+  OceanChange.etd_old_sub_new_unit = val['ETD/ETA_Change'].ocean_etd_old_sub_new_unit
+  OceanChange.ETAradio = val['ETD/ETA_Change'].ocean_eta_change
+  OceanChange.eta_old_sub_new = val['ETD/ETA_Change'].ocean_eta_old_sub_new
+  OceanChange.eta_old_sub_new_unit = val['ETD/ETA_Change'].ocean_eta_old_sub_new_unit
+  OceanETDInit.value = OceanChange
+  let AirChange: any = {}
+  AirChange.ETDradio = val['ETD/ETA_Change'].air_etd_change
+  AirChange.etd_old_sub_new = val['ETD/ETA_Change'].air_etd_old_sub_new
+  AirChange.etd_old_sub_new_unit = val['ETD/ETA_Change'].air_etd_old_sub_new_unit
+  AirChange.ETAradio = val['ETD/ETA_Change'].air_eta_change
+  AirChange.eta_old_sub_new = val['ETD/ETA_Change'].air_eta_old_sub_new
+  AirChange.eta_old_sub_new_unit = val['ETD/ETA_Change'].air_eta_old_sub_new_unit
+  AirETDInit.value = AirChange
+}
+
+// 给tag list赋值
+const ChangeCheckOceanRules = (val: any) => {
+  OceanCheckList.value = val
+}
+const ChangeContainerRules = (val: any) => {
+  ContainerOceanList.value = val
+}
+// delayed赋值
+const ChangeDeayedRules = (val: any) => {
+  DelayedDeparturedList.value = []
+  if (val.Departure != '') {
+    DelayedDeparturedList.value.push(val.Departure)
+    savesubscribeobj.ocean_atd_sub_etd = val.Departure.split(' ')[3]
+    savesubscribeobj.ocean_atd_sub_etd_unit = val.Departure.split(' ')[4]
+  } else {
+    delete savesubscribeobj.ocean_atd_sub_etd
+    delete savesubscribeobj.ocean_atd_sub_etd_unit
+  }
+  if (val.Arrival != '') {
+    DelayedDeparturedList.value.push(val.Arrival)
+    savesubscribeobj.ocean_ata_sub_eta = val.Arrival.split(' ')[4]
+    savesubscribeobj.ocean_ata_sub_eta_unit = val.Arrival.split(' ')[5]
+  } else {
+    delete savesubscribeobj.ocean_ata_sub_eta
+    delete savesubscribeobj.ocean_ata_sub_eta_unit
+  }
+}
+const ChangeAirRules = (val: any) => {
+  DelayedAirdList.value = []
+  if (val.Departure != '') {
+    DelayedAirdList.value.push(val.Departure)
+    savesubscribeobj.air_atd_sub_etd = val.Departure.split(' ')[3]
+    savesubscribeobj.air_atd_sub_etd_unit = val.Departure.split(' ')[4]
+  } else {
+    delete savesubscribeobj.air_atd_sub_etd
+    delete savesubscribeobj.air_atd_sub_etd_unit
+  }
+  if (val.Arrival != '') {
+    DelayedAirdList.value.push(val.Arrival)
+    savesubscribeobj.air_ata_sub_eta = val.Arrival.split(' ')[4]
+    savesubscribeobj.air_ata_sub_eta_unit = val.Arrival.split(' ')[5]
+  } else {
+    delete savesubscribeobj.air_ata_sub_eta
+    delete savesubscribeobj.air_ata_sub_eta_unit
+  }
+}
+const ChangeCheckAirRules = (val: any) => {
+  AirCheckList.value = val
+}
+
+// 更改Frequency时间
+const MilFrequencyList = ref()
+const ChangeMilFrequency = (val: any, type: any) => {
+  MilFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ConFrequencyList = ref()
+const ChangeConFrequency = (val: any, type: any) => {
+  ConFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const DepFrequencyList = ref()
+const ChangeDepFrequency = (val: any, type: any) => {
+  DepFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ETDFrequencyList = ref()
+const ChangeETDFrequency = (val: any, type: any) => {
+  ETDFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+
+// 删除 Frequency tag
+const NotiFrequencyDeleteMil = ref()
+const NotiFrequencyDeleteCon = ref()
+const NotiFrequencyDeleteDep = ref()
+const NotiFrequencyDeleteETD = ref()
+const handleCloseRadio = (val: any) => {
+  if (val == 'Mil') {
+    NotiFrequencyDeleteMil.value.handleCloseRadioFrequency()
+  } else if (val == 'Con') {
+    NotiFrequencyDeleteCon.value.handleCloseRadioFrequency()
+  } else if (val == 'Dep') {
+    NotiFrequencyDeleteDep.value.handleCloseRadioFrequency()
+  } else {
+    NotiFrequencyDeleteETD.value.handleCloseRadioFrequency()
+  }
+}
+
+// methods 切换
+const MilMethodsList = ref()
+const ChangeMethodsAddMil = (val: any, type: any) => {
+  MilMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ConMethodsList = ref()
+const ChangeMethodsAddCon = (val: any, type: any) => {
+  ConMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const DepMethodsList = ref()
+const ChangeMethodsAddDep = (val: any, type: any) => {
+  DepMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ETDMethodsList = ref()
+const ChangeMethodsAddETD = (val: any, type: any) => {
+  ETDMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const OceanDelayed = ref()
+const AirDelayed = ref()
+
+const handleCloseDelayed = (val: any) => {
+  OceanDelayed.value.closeDelayed(val)
+}
+const handleCloseAirDelayed = (val: any) => {
+  AirDelayed.value.closeDelayed(val)
+}
+// ETD Change
+const ChangeETDOceanRules = (val: any) => {
+  ETDOceanList.value = []
+  if (val.ETD != '') {
+    if (val.ETD.indexOf('≥') != -1) {
+      savesubscribeobj.ocean_etd_change = false
+      savesubscribeobj.ocean_etd_old_sub_new = val.ETD.split(' ')[6]
+      savesubscribeobj.ocean_etd_old_sub_new_unit = val.ETD.split(' ')[7]
+    } else {
+      savesubscribeobj.ocean_etd_change = true
+    }
+    ETDOceanList.value.push(val.ETD)
+  } else {
+    delete savesubscribeobj.ocean_etd_change
+    delete savesubscribeobj.ocean_etd_old_sub_new
+    delete savesubscribeobj.ocean_etd_old_sub_new_unit
+  }
+  if (val.ETA != '') {
+    if (val.ETA.indexOf('≥') != -1) {
+      savesubscribeobj.ocean_eta_change = false
+      savesubscribeobj.ocean_eta_old_sub_new = val.ETA.split(' ')[6]
+      savesubscribeobj.ocean_eta_old_sub_new_unit = val.ETA.split(' ')[7]
+    } else {
+      savesubscribeobj.ocean_eta_change = true
+    }
+    ETDOceanList.value.push(val.ETA)
+  } else {
+    delete savesubscribeobj.ocean_eta_change
+    delete savesubscribeobj.ocean_eta_old_sub_new
+    delete savesubscribeobj.ocean_eta_old_sub_new_unit
+  }
+}
+// 删除ETD tag
+const OceanETD = ref()
+const closeOceanETD = (val: any) => {
+  OceanETD.value.closeETD(val)
+}
+// ETA Change
+const ChangeETDAirRules = (val: any) => {
+  ETDAirList.value = []
+  if (val.ETD != '') {
+    if (val.ETD.indexOf('≥') != -1) {
+      savesubscribeobj.air_etd_change = false
+      savesubscribeobj.air_etd_old_sub_new = val.ETD.split(' ')[6]
+      savesubscribeobj.air_etd_old_sub_new_unit = val.ETD.split(' ')[7]
+    } else {
+      savesubscribeobj.air_etd_change = true
+    }
+    ETDAirList.value.push(val.ETD)
+  } else {
+    delete savesubscribeobj.air_etd_change
+    delete savesubscribeobj.air_etd_old_sub_new
+    delete savesubscribeobj.air_etd_old_sub_new_unit
+  }
+  if (val.ETA != '') {
+    if (val.ETA.indexOf('≥') != -1) {
+      savesubscribeobj.air_eta_change = false
+      savesubscribeobj.air_eta_old_sub_new = val.ETA.split(' ')[6]
+      savesubscribeobj.air_eta_old_sub_new_unit = val.ETA.split(' ')[7]
+    } else {
+      savesubscribeobj.air_eta_change = true
+    }
+    ETDAirList.value.push(val.ETA)
+  } else {
+    delete savesubscribeobj.air_eta_change
+    delete savesubscribeobj.air_eta_old_sub_new
+    delete savesubscribeobj.air_eta_old_sub_new_unit
+  }
+}
+
+// 删除ETA tag
+const AirETD = ref()
+const closeAirETD = (val: any) => {
+  AirETD.value.closeETD(val)
+}
+
+const emits = defineEmits(['UnsavedCollapse', 'SavedAddedRules'])
+// 不保存修改的折叠面板
+const UnsavedCollapse = () => {
+  CancelRulesVisible.value = false
+  emits('UnsavedCollapse')
+}
+
+// 保存subscribe配置
+const missingmessage = ref('')
+// 保存成功调用接口
+const SaveSuceessful = () => {
+  $api
+    .Savesubscribe({
+      ...savesubscribeobj
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        SaveedVisible.value = true
+        setTimeout(() => {
+          SaveedVisible.value = false
+          emits('SavedAddedRules', res.data.addedRules, savesubscribeobj.rules_type)
+        }, 3000)
+      }
+    })
+}
+const Savesubscribe = () => {
+  let str = ''
+  if (props.TitleType == 'Milestone') {
+    savesubscribeobj.rules_type = 'Milestone_Update'
+    if (
+      OceanCheckList.value == undefined ||
+      AirCheckList.value == undefined ||
+      MilFrequencyList.value.length == 0 ||
+      MilMethodsList.value.length == 0
+    ) {
+      if (OceanCheckList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (AirCheckList.value == undefined) {
+        missingmessage.value += 'Air Shipments, '
+      }
+      if (MilFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (MilMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      savesubscribeobj.ocean_milestone = OceanCheckList.value
+      savesubscribeobj.air_milestone = AirCheckList.value
+      str =
+        'Ocean Milestones: ' +
+        OceanCheckList.value.join(',') +
+        ';\nAir Milestones: ' +
+        AirCheckList.value.join(',') +
+        ';'
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  } else if (props.TitleType == 'Container') {
+    savesubscribeobj.rules_type = 'Container_Status_Update'
+    if (
+      ContainerOceanList.value == undefined ||
+      ConFrequencyList.value.length == 0 ||
+      ConMethodsList.value.length == 0
+    ) {
+      if (ContainerOceanList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (ConFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (ConMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      savesubscribeobj.ocean_ctnr_status = ContainerOceanList.value
+      str = 'Ocean Container: ' + ContainerOceanList.value.join(',')
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  } else if (props.TitleType == 'Departure') {
+    savesubscribeobj.rules_type = 'Departure/Arrival_Delay'
+    if (
+      DelayedDeparturedList.value == undefined ||
+      DelayedAirdList.value == undefined ||
+      DepFrequencyList.value.length == 0 ||
+      DepMethodsList.value.length == 0
+    ) {
+      if (DelayedDeparturedList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (DelayedAirdList.value == undefined) {
+        missingmessage.value += 'Air Shipments, '
+      }
+      if (DepFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (DepMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      str = DelayedDeparturedList.value.join(';\n') + ';\n' + DelayedAirdList.value.join(';\n')
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  } else {
+    savesubscribeobj.rules_type = 'ETD/ETA_Change'
+    if (
+      ETDOceanList.value == undefined ||
+      ETDOceanList.value.length == 0 ||
+      ETDAirList.value == undefined ||
+      ETDAirList.value.length == 0 ||
+      ETDFrequencyList.value.length == 0 ||
+      ETDMethodsList.value.length == 0
+    ) {
+      if (ETDOceanList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (ETDAirList.value == undefined) {
+        missingmessage.value += 'Air Shipments, '
+      }
+      if (ETDFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (ETDMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      str = ETDOceanList.value.join(';\n') + ';\n' + ETDAirList.value.join(';\n')
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  }
+}
+
+// 删除表格数据后清空所有数据
+const clearData = (val: any) => {
+  if (val == 'Milestone_Update') {
+    OceanCheckList.value = []
+    AirCheckList.value = []
+    MilFrequencyList.value = []
+    MilMethodsList.value = []
+    MilestoneOceanListChecked.value = []
+    MilestoneAirListChecked.value = []
+    handleCloseRadio('Mil')
+  } else if (val == 'Container_Status_Update') {
+    ContainerOceanList.value = []
+    ConFrequencyList.value = []
+    ConMethodsList.value = []
+    ContainerOceanListChecked.value = []
+    handleCloseRadio('Con')
+  } else if (val == 'Departure/Arrival_Delay') {
+    DelayedDeparturedList.value = []
+    DelayedAirdList.value = []
+    DepFrequencyList.value = []
+    DepMethodsList.value = []
+    OceanDelayed.value.ClearData()
+    AirDelayed.value.ClearData()
+    handleCloseRadio('Dep')
+  } else {
+    ETDOceanList.value = []
+    ETDAirList.value = []
+    ETDFrequencyList.value = []
+    ETDMethodsList.value = []
+    handleCloseRadio('ETD')
+    OceanETD.value.ClearData()
+    AirETD.value.ClearData()
+  }
+}
+
+defineExpose({
+  clearData
+})
+</script>
+<template>
+  <div class="Rules_flex">
+    <div class="Rules_left">
+      <div class="Rules_collapse">
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'Milestone'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Milestone
+              </div>
+            </template>
+            <div>
+              <RulesShipments
+                Title="Ocean shipments"
+                @ChangeCheckRules="ChangeCheckOceanRules"
+                :CheckboxList="MilestoneOceanListInit"
+                :CheckedList="MilestoneOceanListChecked"
+              ></RulesShipments>
+            </div>
+            <div>
+              <RulesShipments
+                Title="Air shipments"
+                @ChangeCheckRules="ChangeCheckAirRules"
+                :CheckboxList="MilestoneAirListInit"
+                :CheckedList="MilestoneAirListChecked"
+              ></RulesShipments>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'Container'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Container Status
+              </div>
+            </template>
+            <div>
+              <RulesShipments
+                Title="Ocean shipments"
+                @ChangeCheckRules="ChangeContainerRules"
+                :CheckboxList="ContainerOceanListInit"
+                :CheckedList="ContainerOceanListChecked"
+              ></RulesShipments>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'Departure'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Delayed Type
+              </div>
+            </template>
+            <div>
+              <DelayedType
+                Title="Ocean shipments"
+                ref="OceanDelayed"
+                :DelayedData="DelayedDataInit"
+                @ChangeCheckRules="ChangeDeayedRules"
+              ></DelayedType>
+            </div>
+            <div>
+              <DelayedType
+                Title="Air shipments"
+                ref="AirDelayed"
+                :DelayedData="DelayedDataInitAir"
+                @ChangeCheckRules="ChangeAirRules"
+              ></DelayedType>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'ETDChange'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Delayed Type
+              </div>
+            </template>
+            <div>
+              <ETDShipments
+                Title="Ocean shipments"
+                ref="OceanETD"
+                :ETDData="OceanETDInit"
+                @ChangeCheckRules="ChangeETDOceanRules"
+              ></ETDShipments>
+            </div>
+            <div>
+              <ETDShipments
+                Title="Air shipments"
+                ref="AirETD"
+                :ETDData="AirETDInit"
+                @ChangeCheckRules="ChangeETDAirRules"
+              ></ETDShipments>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          style="margin-top: 17px; margin-right: 16px"
+          v-model="RulesActive"
+          @change="IsTwoActive = !IsTwoActive"
+        >
+          <el-collapse-item name="NotificationFrequency">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsTwoActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Notification Frequency
+              </div>
+            </template>
+            <NotiFrequency
+              v-if="props.TitleType == 'Milestone'"
+              ref="NotiFrequencyDeleteMil"
+              :FrequencyData="FrequencyDataMil"
+              @ChangeFrequencyAdd="ChangeMilFrequency"
+            ></NotiFrequency>
+            <NotiFrequency
+              v-if="props.TitleType == 'Container'"
+              ref="NotiFrequencyDeleteCon"
+              :FrequencyData="FrequencyDataCon"
+              @ChangeFrequencyAdd="ChangeConFrequency"
+            ></NotiFrequency>
+            <NotiFrequency
+              v-if="props.TitleType == 'Departure'"
+              ref="NotiFrequencyDeleteDep"
+              :FrequencyData="FrequencyDataDep"
+              @ChangeFrequencyAdd="ChangeDepFrequency"
+            ></NotiFrequency>
+            <NotiFrequency
+              v-if="props.TitleType == 'ETDChange'"
+              ref="NotiFrequencyDeleteETD"
+              :FrequencyData="FrequencyDataETD"
+              @ChangeFrequencyAdd="ChangeETDFrequency"
+            ></NotiFrequency>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          style="margin: 17px 0"
+          v-model="RulesActive"
+          @change="IsThreeActive = !IsThreeActive"
+        >
+          <el-collapse-item name="NotificationMethod">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsThreeActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Notification Method
+              </div>
+            </template>
+            <NotiMethods
+              v-if="props.TitleType == 'Milestone'"
+              :MethodsData="MethodsDataMil"
+              @ChangeMethodsAdd="ChangeMethodsAddMil"
+            ></NotiMethods>
+            <NotiMethods
+              v-if="props.TitleType == 'Container'"
+              :MethodsData="MethodsDataCon"
+              @ChangeMethodsAdd="ChangeMethodsAddCon"
+            ></NotiMethods>
+            <NotiMethods
+              v-if="props.TitleType == 'Departure'"
+              :MethodsData="MethodsDataDep"
+              @ChangeMethodsAdd="ChangeMethodsAddDep"
+            ></NotiMethods>
+            <NotiMethods
+              v-if="props.TitleType == 'ETDChange'"
+              :MethodsData="MethodsDataETD"
+              @ChangeMethodsAdd="ChangeMethodsAddETD"
+            ></NotiMethods>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+    </div>
+    <div class="Rules_right">
+      <div class="right_Title">Added Rules</div>
+      <AddedrluesTag
+        :CheckedList="DelayedDeparturedList"
+        v-if="props.TitleType == 'Departure'"
+        @handleCloseRadio="handleCloseDelayed"
+        Title="Ocean Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="OceanCheckList"
+        Title="Ocean Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Container'"
+        :CheckedList="ContainerOceanList"
+        Title="Ocean Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        :CheckedList="ETDOceanList"
+        v-if="props.TitleType == 'ETDChange'"
+        Title="Ocean Shipments"
+        @handleCloseRadio="closeOceanETD"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        :CheckedList="ETDAirList"
+        v-if="props.TitleType == 'ETDChange'"
+        @handleCloseRadio="closeAirETD"
+        Title="Air Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        :CheckedList="DelayedAirdList"
+        v-if="props.TitleType == 'Departure'"
+        @handleCloseRadio="handleCloseAirDelayed"
+        Title="Air Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="AirCheckList"
+        Title="Air Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="MilFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('Mil')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Container'"
+        :CheckedList="ConFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('Con')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Departure'"
+        :CheckedList="DepFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('Dep')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'ETDChange'"
+        :CheckedList="ETDFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('ETD')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="MilMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Container'"
+        :CheckedList="ConMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Departure'"
+        :CheckedList="DepMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'ETDChange'"
+        :CheckedList="ETDMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+    </div>
+  </div>
+  <div class="Rules_buttom">
+    <el-button class="el-button--dark rules_button" @click="Savesubscribe">Save</el-button>
+    <el-button @click="CancelRulesVisible = true" class="rules_button" type="default"
+      >Cancel</el-button
+    >
+    <!-- 取消保存 -->
+    <el-dialog v-model="CancelRulesVisible" width="480">
+      <div>You have unsaved changes.</div>
+      <div>Are you sure you want to leave this page?</div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="default" @click="CancelRulesVisible = false" style="width: 100px"
+            >Cancel</el-button
+          >
+          <el-button class="el-button--warning" @click="UnsavedCollapse" style="width: 100px">
+            OK
+          </el-button>
+        </div>
+      </template>
+      <template #header>
+        <div class="cancel_header">
+          <span class="iconfont_icon iconfont_warning">
+            <svg class="iconfont icon_warning" aria-hidden="true">
+              <use xlink:href="#icon-icon_tipsfilled_b"></use>
+            </svg>
+          </span>
+          Unsaved Changes
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 保存失败 -->
+    <el-dialog v-model="UnableSaveVisible" width="480">
+      <div>{{ missingmessage }} missing.</div>
+      <div>Please complete all required fields.</div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button
+            class="el-button--danger"
+            @click="UnableSaveVisible = false"
+            style="width: 100px"
+          >
+            OK
+          </el-button>
+        </div>
+      </template>
+      <template #header>
+        <div class="cancel_header">
+          <span class="iconfont_icon iconfont_warning">
+            <svg class="iconfont icon_danger" aria-hidden="true">
+              <use xlink:href="#icon-icon_fail_fill_b"></use>
+            </svg>
+          </span>
+          Unable to Save
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 保存成功 -->
+    <el-dialog v-model="SaveedVisible" width="320" style="height: 212px">
+      <div style="text-align: center"><el-image :src="submitsucessful" /></div>
+      <div style="text-align: center; margin-top: 20px">Saved successfully</div>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.Rules_flex {
+  padding: 0 0 0 8px;
+  display: flex;
+}
+.Rules_left {
+  width: 60%;
+  border-right: 1px solid var(--color-user-config-title-bottom-border);
+}
+.Rules_right {
+  width: 40%;
+  background-color: var(--more-filters-background-color);
+}
+.right_Title {
+  color: var(--color-neutral-1);
+  padding: 9px 8px;
+  font-size: 18px;
+  font-weight: 700;
+  border-bottom: 1px solid var(--color-user-config-title-bottom-border);
+  background-color: var(--color-drawer-body-bg);
+}
+.iconfont {
+  width: 16px;
+  height: 16px;
+  margin-right: 8px;
+}
+.icon_danger {
+  fill: var(--color-btn-danger-bg);
+}
+:deep(.Rules_Title) {
+  color: var(--color-neutral-1);
+  font-size: 14px;
+  font-weight: 700;
+  height: 22px;
+  display: flex;
+  align-items: center;
+}
+.stars_red {
+  color: var(--color-danger);
+}
+:deep(.el-collapse-item__header) {
+  height: 25px !important;
+  margin: 14px 0 0 0 !important;
+  border: none !important;
+  padding: 0 !important;
+}
+:deep(.el-collapse-item__header):hover {
+  background-color: #fff !important;
+  border: none !important;
+}
+:deep(.el-collapse-item__arrow) {
+  width: 0 !important;
+  height: 0 !important;
+}
+:deep(.el-icon svg) {
+  width: 0 !important;
+}
+:deep(.el-collapse-item__header.is-active) {
+  background-color: transparent !important;
+  border-color: transparent !important;
+}
+:deep(.el-collapse-item__arrow.is-active) {
+  transform: rotate(-180deg) !important;
+}
+:deep(.Ocean_collapse .el-collapse-item__arrow) {
+  width: 16px !important;
+  height: 16px !important;
+  background-image: url('../src/images/icon_expand.png') !important;
+  background-size: contain;
+  background-repeat: no-repeat;
+  transform: rotate(0);
+}
+:deep(.Ocean_collapse .el-collapse-item__header) {
+  background-color: #fff !important;
+  padding: 0 8px !important;
+  height: 40px !important;
+}
+:deep(.Ocean_collapse .el-collapse-item) {
+  background-color: var(--color-dialog-header-bg);
+  border-radius: 12px;
+}
+:deep(.Ocean_collapse .el-collapse-item__wrap) {
+  padding: 0 8px !important;
+}
+:deep(.Ocean_collapse .el-collapse-item__header.is-active) {
+  background-color: var(--color-dialog-header-bg) !important;
+}
+:deep(.el-checkbox__input.is-checked + .el-checkbox__label) {
+  color: var(--color-neutral-1);
+}
+.Rules_buttom {
+  padding: 8px;
+  border-top: 1px solid var(--color-border-1);
+}
+.rules_button {
+  width: 100px;
+  height: 40px;
+}
+.cancel_header {
+  font-size: 18px;
+  font-weight: 700;
+  color: var(--color-neutral-1);
+  display: flex;
+  align-items: center;
+}
+.icon_warning {
+  width: 22px;
+  height: 22px;
+  margin-right: 0;
+  fill: var(--color-btn-warning-bg);
+}
+.iconfont_warning {
+  display: flex;
+  align-items: center;
+}
+:deep(header.el-dialog__header) {
+  background-color: #fff;
+}
+:deep(footer.el-dialog__footer) {
+  border-top: none;
+}
+:deep(.el-collapse) {
+  margin-right: 8px;
+}
+</style>

+ 78 - 0
src/components/AddRules/src/components/AddedrluesTag.vue

@@ -0,0 +1,78 @@
+<script setup lang="ts" >
+import { ref, watch } from 'vue'
+const props = defineProps({
+  CheckedList: Array,
+  Title: String
+})
+const CheckedList = ref(props.CheckedList)
+watch(
+  () => props.CheckedList,
+  (current) => {
+    CheckedList.value = current
+  }
+)
+
+const emit = defineEmits(['handleCloseRadio'])
+
+const handleClose = (tag: any) => {
+  CheckedList.value?.splice(CheckedList.value.indexOf(tag), 1)
+  emit('handleCloseRadio', tag)
+}
+</script>
+
+<template>
+  <div>
+    <el-card class="Rules_Card">
+      <div class="Card_Title">{{ props.Title }}</div>
+      <div class="right_flex">
+        <el-tag
+          class="tag"
+          v-for="(tag, index) in CheckedList"
+          :key="index"
+          closable
+          @close="handleClose(tag)"
+        >
+          {{ tag }}
+        </el-tag>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.tag {
+  border-radius: 3px;
+  margin-bottom: 4px;
+  color: var(--color-neutral-1);
+  height: 32px;
+  width: fit-content;
+  font-weight: 400;
+  font-size: 14px;
+  background-color: var(--tag-bg-color) !important;
+  border-color: var(--tag-bg-color);
+}
+.tag:last-child {
+  margin-bottom: 0;
+}
+:deep(.el-card__body) {
+  padding: 8px !important;
+  max-height: 400px;
+  overflow-y: scroll;
+}
+:deep(.el-tag .el-tag__close svg) {
+  width: 16px !important;
+}
+.right_flex {
+  display: flex;
+  flex-direction: column;
+}
+.Rules_Card {
+  margin: 8px;
+}
+.Card_Title {
+  color: var(--color-neutral-1);
+  font-size: 14px;
+  font-weight: 600;
+  margin: 9px 0 11px 0;
+}
+</style>

+ 279 - 0
src/components/AddRules/src/components/DelayedType.vue

@@ -0,0 +1,279 @@
+<script lang="ts" setup>
+import { ref, computed, watch } from 'vue'
+
+const props = defineProps({
+  Title: String,
+  DelayedData: Object
+})
+const delayed_data = ref(props.DelayedData)
+watch(
+  () => props.DelayedData,
+  (current) => {
+    delayed_data.value = current
+    DelayedInit()
+  }
+)
+
+const DelayedInit = () => {
+  let array2: any = []
+  OceanCheckedList.value = []
+  if (delayed_data.value?.atd_etd != '' && delayed_data.value?.atd_etd != undefined) {
+    isDeparture.value = true
+    DepartureTime.value = delayed_data.value?.atd_etd
+    DepartureSelect.value = delayed_data.value?.atd_etd_unit
+    OceanCheckedList.value.push('Departure Delayed')
+    array2.push('Departure')
+  }
+  if (delayed_data.value?.ata_eta != '' && delayed_data.value?.ata_eta != undefined) {
+    isArrival.value = true
+    ArrivalTime.value = delayed_data.value?.ata_eta
+    ArrivalSelect.value = delayed_data.value?.ata_eta_unit
+    OceanCheckedList.value.push('Arrival Delayed (ATA-ETA)')
+    array2.push('Arrival')
+  }
+  CheckChange(OceanCheckedList.value)
+  changedeparture(array2)
+}
+const isDeparture = ref(false)
+const isArrival = ref(false)
+const OceanActive = ref(['DelayedShipments'])
+const OceanCheckedList = ref()
+const DepartureTime = ref('')
+const DepartureSelect = ref('')
+const ArrivalTime = ref('')
+const ArrivalSelect = ref('')
+const emit = defineEmits(['ChangeCheckRules', 'closeDelayed'])
+const DepartureList = ref({
+  Departure: '',
+  Arrival: ''
+})
+let Departurestr: any = ''
+let Arrivalstr: any = ''
+
+const clampedValue = computed(() => {
+  if (DepartureTime.value == '') {
+    return 0
+  } else {
+    const numericValue = DepartureTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+const clampedArrivalValue = computed(() => {
+  if (ArrivalTime.value == '') {
+    return 0
+  } else {
+    const numericValue = ArrivalTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+
+const CheckChange = (val: any) => {
+  if (val.includes('Departure Delayed')) {
+    isDeparture.value = true
+    Departurestr = 'Departure Delayed' + ' ≥ ' + clampedValue.value + ' ' + DepartureSelect.value
+    if (DepartureSelect.value != '') {
+      DepartureList.value.Departure = Departurestr
+    }
+    if (val.includes('Arrival Delayed (ATA-ETA)')) {
+      isArrival.value = true
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    } else {
+      isArrival.value = false
+      DepartureList.value.Arrival = ''
+    }
+  } else {
+    isDeparture.value = false
+    DepartureList.value.Departure = ''
+    if (val.includes('Arrival Delayed (ATA-ETA)')) {
+      isArrival.value = true
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    } else {
+      isArrival.value = false
+      DepartureList.value.Arrival = ''
+    }
+  }
+  emit('ChangeCheckRules', DepartureList.value)
+}
+const changedeparture = (val: any) => {
+  if (val == 'Departure') {
+    Departurestr = 'Departure Delayed' + ' ≥ ' + clampedValue.value + ' ' + DepartureSelect.value
+    if (DepartureSelect.value != '') {
+      DepartureList.value.Departure = Departurestr
+    }
+    if (val == 'Arrival') {
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    }
+  } else {
+    if (val == 'Arrival') {
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    }
+  }
+  emit('ChangeCheckRules', DepartureList.value)
+}
+const closeDelayed = (val: any) => {
+  if (val.includes('Departure')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('Departure Delayed'), 1)
+    DepartureList.value.Departure = ''
+    isDeparture.value = false
+    DepartureTime.value = ''
+    DepartureSelect.value = ''
+  }
+  if (val.includes('Arrival')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('Arrival Delayed (ATA-ETA)'), 1)
+    DepartureList.value.Arrival = ''
+    isArrival.value = false
+    ArrivalTime.value = ''
+    ArrivalSelect.value = ''
+  }
+  emit('ChangeCheckRules', DepartureList.value)
+}
+
+// 清除所有数据
+const ClearData = () => {
+  OceanCheckedList.value = []
+  DepartureList.value.Departure = ''
+  isDeparture.value = false
+  DepartureList.value.Arrival = ''
+  isArrival.value = false
+  DepartureTime.value = ''
+  DepartureSelect.value = ''
+  ArrivalTime.value = ''
+  ArrivalSelect.value = ''
+}
+
+defineExpose({
+  closeDelayed,
+  ClearData
+})
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="DelayedShipments">
+        <template #title>
+          <div class="Rules_Title OceanTitle">{{ props.Title }}</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
+            <el-checkbox class="delayedType" value="Departure Delayed">
+              <div>Departure Delayed (ATD-ETD)</div>
+              <div v-if="isDeparture" style="margin-top: 16px">
+                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedIcon">></span>
+                <el-input
+                  v-model="DepartureTime"
+                  class="input-with-select"
+                  :value="clampedValue"
+                  @input="changedeparture('Departure')"
+                >
+                  <template #append>
+                    <el-select
+                      v-model="DepartureSelect"
+                      placeholder="Select"
+                      @change="changedeparture('Departure')"
+                    >
+                      <el-option label="Day(s)" value="Day(s)" />
+                      <el-option label="Hour(s)" value="Hour(s)" />
+                    </el-select>
+                  </template>
+                </el-input>
+              </div>
+            </el-checkbox>
+            <el-checkbox class="delayedType" value="Arrival Delayed (ATA-ETA)">
+              <div>Arrival Delayed (ATA-ETA)</div>
+              <div v-if="isArrival" style="margin-top: 16px">
+                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedIcon">></span>
+                <el-input
+                  v-model="ArrivalTime"
+                  @input="changedeparture('Arrival')"
+                  :value="clampedArrivalValue"
+                  class="input-with-select"
+                >
+                  <template #append>
+                    <el-select
+                      v-model="ArrivalSelect"
+                      placeholder="Select"
+                      @change="changedeparture('Arrival')"
+                    >
+                      <el-option label="Day(s)" value="Day(s)" />
+                      <el-option label="Hour(s)" value="Hour(s)" />
+                    </el-select>
+                  </template>
+                </el-input>
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+.delayedType {
+  align-items: start;
+  height: fit-content;
+  margin-right: 5px;
+  border-radius: 6px;
+  padding: 8px;
+}
+.input-with-select {
+  width: 180px;
+}
+:deep(.el-input__wrapper) {
+  width: 50%;
+}
+:deep(.el-input-group__append) {
+  width: 50%;
+  padding: 0;
+  border: none;
+}
+:deep(.el-input-group--append .el-input-group__append .el-select .el-select__wrapper) {
+  padding: 5px 10px;
+  background-color: #fff;
+}
+.delayedTitle {
+  color: var(--color-neutral-2);
+  font-size: 12px;
+}
+.delayedIcon {
+  margin: 0 8px;
+}
+.oceanCheckbox {
+  margin-bottom: 8px;
+}
+</style>

+ 341 - 0
src/components/AddRules/src/components/ETDShipments.vue

@@ -0,0 +1,341 @@
+<script lang="ts" setup>
+import { ref, computed, watch } from 'vue'
+
+const props = defineProps({
+  Title: String,
+  ETDData: Object
+})
+const ETD_data = ref(props.ETDData)
+watch(
+  () => props.ETDData,
+  (current) => {
+    ETD_data.value = current
+    ETDInit()
+  }
+)
+
+const ETDInit = () => {
+  OceanCheckedList.value = []
+  if (ETD_data.value?.ETDradio == 't') {
+    ETDRadio.value = '1'
+    OceanCheckedList.value.push('ETD')
+  } else {
+    if (ETD_data.value?.etd_old_sub_new != '' && ETD_data.value?.etd_old_sub_new != undefined) {
+      ETDRadio.value = '2'
+      isETD.value = true
+      ETDTime.value = ETD_data.value?.etd_old_sub_new
+      ETDSelect.value = ETD_data.value?.etd_old_sub_new_unit
+      OceanCheckedList.value.push('ETD')
+    }
+  }
+  if (ETD_data.value?.ETAradio == 't') {
+    ETARadio.value = '1'
+    OceanCheckedList.value.push('ETA')
+  } else {
+    if (ETD_data.value?.eta_old_sub_new != '' && ETD_data.value?.eta_old_sub_new != undefined) {
+      ETARadio.value = '2'
+      isETA.value = true
+      ETATime.value = ETD_data.value?.eta_old_sub_new
+      ETASelect.value = ETD_data.value?.eta_old_sub_new_unit
+      OceanCheckedList.value.push('ETA')
+    }
+  }
+  CheckChange(OceanCheckedList.value)
+  changeETDRadio(ETDRadio.value)
+  changeETARadio(ETARadio.value)
+  changedeparture(OceanCheckedList.value)
+}
+const isETD = ref(false)
+const isETA = ref(false)
+const OceanActive = ref(['ETDShipments'])
+const OceanCheckedList = ref()
+const ETDTime = ref('')
+const ETDSelect = ref('')
+const ETATime = ref('')
+const ETASelect = ref('')
+const ETDRadio = ref()
+const ETARadio = ref()
+const emit = defineEmits(['ChangeCheckRules', 'closeETD'])
+const ETDETAList = ref({
+  ETD: '',
+  ETA: ''
+})
+let ETDstr: any = ''
+let ETAstr: any = ''
+const CheckChange = (val: any) => {
+  if (val.includes('ETD')) {
+    isETD.value = true
+    if (ETDSelect.value != '') {
+      ETDETAList.value.ETD = ETDstr
+    }
+    if (val.includes('ETA')) {
+      isETA.value = true
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    } else {
+      isETA.value = false
+      ETDETAList.value.ETA = ''
+    }
+  } else {
+    isETD.value = false
+    ETDETAList.value.ETD = ''
+    if (val.includes('ETA')) {
+      isETA.value = true
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    } else {
+      isETA.value = false
+      ETDETAList.value.ETA = ''
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const changeETDRadio = (val: any) => {
+  if (val == 1) {
+    ETDstr = 'ETD: Notify for all changes'
+    ETDETAList.value.ETD = ETDstr
+  } else if (val == 2) {
+    ETDstr = 'ETD: Notify for all changes ≥ ' + clampedValue.value + ' ' + ETDSelect.value
+    if (ETDSelect.value != '') {
+      ETDETAList.value.ETD = ETDstr
+    } else {
+      ETDETAList.value.ETD = ''
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const changeETARadio = (val: any) => {
+  if (val == 1) {
+    ETAstr = 'ETA: Notify for all changes'
+    ETDETAList.value.ETA = ETAstr
+  } else if (val == 2) {
+    ETAstr = 'ETA: Notify for all changes ≥ ' + clampedETAValue.value + ' ' + ETASelect.value
+    if (ETASelect.value != '') {
+      ETDETAList.value.ETA = ETAstr
+    } else {
+      ETDETAList.value.ETA = ''
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const changedeparture = (val: any) => {
+  if (val == 'ETD') {
+    ETDstr = 'ETD: Notify for all changes ≥ ' + clampedValue.value + ' ' + ETDSelect.value
+    if (ETDSelect.value != '') {
+      ETDETAList.value.ETD = ETDstr
+    }
+    if (val == 'ETA') {
+      ETAstr = 'ETA: Notify for all changes ≥ ' + clampedETAValue.value + ' ' + ETASelect.value
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    }
+  } else {
+    if (val == 'ETA') {
+      ETAstr = 'ETA: Notify for all changes ≥ ' + clampedETAValue.value + ' ' + ETASelect.value
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const closeETD = (val: any) => {
+  if (val.includes('ETD')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('ETD'), 1)
+    ETDETAList.value.ETD = ''
+    isETD.value = false
+    ETDTime.value = ''
+    ETDSelect.value = ''
+    ETDRadio.value = 0
+  }
+  if (val.includes('ETA')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('ETA'), 1)
+    ETDETAList.value.ETA = ''
+    isETA.value = false
+    ETATime.value = ''
+    ETASelect.value = ''
+    ETARadio.value = 0
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+
+// 清除所有数据
+const ClearData = () => {
+  OceanCheckedList.value = []
+  ETDETAList.value.ETD = ''
+  isETD.value = false
+  ETDETAList.value.ETA = ''
+  isETA.value = false
+  ETDRadio.value = 0
+  ETARadio.value = 0
+  ETDTime.value = ''
+  ETDSelect.value = ''
+  ETATime.value = ''
+  ETASelect.value = ''
+}
+
+defineExpose({
+  closeETD,
+  ClearData
+})
+
+const clampedValue = computed(() => {
+  if (ETDTime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETDTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+const clampedETAValue = computed(() => {
+  if (ETATime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETATime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="ETDShipments">
+        <template #title>
+          <div class="Rules_Title OceanTitle">{{ props.Title }}</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
+            <el-checkbox class="delayedType" value="ETD">
+              <div>ETD</div>
+              <div v-if="isETD" style="margin-top: 16px">
+                <el-radio-group v-model="ETDRadio" @change="changeETDRadio">
+                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="2"
+                    >Notify only when time difference
+                    <span class="delayedIcon">></span>
+                    <el-input
+                      v-model="ETDTime"
+                      :value="clampedValue"
+                      class="input-with-select"
+                      @input="changedeparture('ETD')"
+                    >
+                      <template #append>
+                        <el-select
+                          v-model="ETDSelect"
+                          placeholder="Select"
+                          @change="changedeparture('ETD')"
+                        >
+                          <el-option label="Day(s)" value="Day(s)" />
+                          <el-option label="Hour(s)" value="Hour(s)" />
+                        </el-select>
+                      </template> </el-input
+                  ></el-radio>
+                </el-radio-group>
+              </div>
+            </el-checkbox>
+            <el-checkbox class="delayedType" value="ETA">
+              <div>ETA</div>
+              <div v-if="isETA" style="margin-top: 16px">
+                <el-radio-group v-model="ETARadio" @change="changeETARadio">
+                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="2"
+                    >Notify only when time difference
+                    <span class="delayedIcon">></span>
+                    <el-input
+                      v-model="ETATime"
+                      :value="clampedETAValue"
+                      class="input-with-select"
+                      @input="changedeparture('ETA')"
+                    >
+                      <template #append>
+                        <el-select
+                          v-model="ETASelect"
+                          placeholder="Select"
+                          @change="changedeparture('ETA')"
+                        >
+                          <el-option label="Day(s)" value="Day(s)" />
+                          <el-option label="Hour(s)" value="Hour(s)" />
+                        </el-select>
+                      </template> </el-input
+                  ></el-radio>
+                </el-radio-group>
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+:deep(.el-radio) {
+  border: none !important;
+}
+.delayedType {
+  align-items: start;
+  height: fit-content;
+  margin-right: 5px;
+  border-radius: 6px;
+  padding: 8px;
+}
+.input-with-select {
+  width: 180px;
+}
+:deep(.el-input__wrapper) {
+  width: 50%;
+}
+:deep(.el-input-group__append) {
+  width: 50%;
+  padding: 0;
+  border: none;
+}
+:deep(.el-input-group--append .el-input-group__append .el-select .el-select__wrapper) {
+  padding: 5px 10px;
+  background-color: #fff;
+}
+.delayedIcon {
+  margin: 0 8px;
+}
+.oceanCheckbox {
+  margin-bottom: 8px;
+}
+:deep(.el-radio-group) {
+  display: block;
+}
+:deep(.el-radio) {
+  display: flex;
+  min-height: 32px;
+  border: 1px solid var(--color-select-border);
+  margin-bottom: 4px;
+  border-radius: 6px;
+  padding: 0 8px;
+  margin-right: 0;
+  height: fit-content;
+  line-height: 32px;
+  align-items: center;
+}
+:deep(.el-radio__input.is-checked + .el-radio__label) {
+  color: var(--color-neutral-1);
+}
+</style>

+ 398 - 0
src/components/AddRules/src/components/NotiFrequency.vue

@@ -0,0 +1,398 @@
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import moment from 'moment-timezone'
+import { defaultTimeZone } from '@/utils/timezone'
+
+const props = defineProps({
+  FrequencyData: Object
+})
+
+const frequency_data = ref(props.FrequencyData)
+const radio = ref(0)
+const FrequencyList = ref()
+const DailyTime = ref('')
+const WeeklyTime = ref('')
+const WeeklyDay = ref('')
+const WeekDay = ref([
+  {
+    label: 'Monday',
+    value: 'Monday'
+  },
+  {
+    label: 'Tuesday',
+    value: 'Tuesday'
+  },
+  {
+    label: 'Wednesday',
+    value: 'Wednesday'
+  },
+  {
+    label: 'Thursday',
+    value: 'Thursday'
+  },
+  {
+    label: 'Friday',
+    value: 'Friday'
+  },
+  {
+    label: 'Saturday',
+    value: 'Saturday'
+  },
+  {
+    label: 'Sunday',
+    value: 'Sunday'
+  }
+])
+const TimeZone = ref([
+  {
+    label: 'UTC-08',
+    value: 'UTC-08'
+  },
+  {
+    label: 'UTC-07',
+    value: 'UTC-07'
+  },
+  {
+    label: 'UTC-06',
+    value: 'UTC-06'
+  },
+  {
+    label: 'UTC-05',
+    value: 'UTC-05'
+  },
+  {
+    label: 'UTC-04',
+    value: 'UTC-04'
+  },
+  {
+    label: 'UTC-03',
+    value: 'UTC-03'
+  },
+  {
+    label: 'UTC-02',
+    value: 'UTC-02'
+  },
+  {
+    label: 'UTC-01',
+    value: 'UTC-01'
+  },
+  {
+    label: 'UTC-00',
+    value: 'UTC-00'
+  },
+  {
+    label: 'UTC+01',
+    value: 'UTC+01'
+  },
+  {
+    label: 'UTC+02',
+    value: 'UTC+02'
+  },
+  {
+    label: 'UTC+03',
+    value: 'UTC+03'
+  },
+  {
+    label: 'UTC+04',
+    value: 'UTC+04'
+  },
+  {
+    label: 'UTC+05',
+    value: 'UTC+05'
+  },
+  {
+    label: 'UTC+05:30',
+    value: 'UTC+05:30'
+  },
+  {
+    label: 'UTC+06',
+    value: 'UTC+06'
+  },
+  {
+    label: 'UTC+07',
+    value: 'UTC+07'
+  },
+  {
+    label: 'UTC+08',
+    value: 'UTC+08'
+  },
+  {
+    label: 'UTC+09',
+    value: 'UTC+09'
+  },
+  {
+    label: 'UTC+10',
+    value: 'UTC+10'
+  },
+  {
+    label: 'UTC+11',
+    value: 'UTC+11'
+  },
+  {
+    label: 'UTC+12',
+    value: 'UTC+12'
+  }
+])
+const TimeZoneDailySelect = ref()
+const TimeZoneWeeklySelect = ref()
+TimeZoneDailySelect.value = 'UTC' + moment().tz(moment.tz.guess()).format('Z').slice(0, 3)
+TimeZoneWeeklySelect.value = 'UTC' + moment().tz(moment.tz.guess()).format('Z').slice(0, 3)
+const isDaily = ref(false)
+const isWeekly = ref(false)
+let savesubscribeobj: any = {}
+
+watch(
+  () => props.FrequencyData,
+  (current) => {
+    frequency_data.value = current
+    FrequencyDataInit()
+  }
+)
+
+const emits = defineEmits(['ChangeFrequencyAdd'])
+
+// 初始设置Frequency
+const FrequencyDataInit = () => {
+  if (frequency_data.value?.frequency_type == 'Instant') {
+    radio.value = 1
+    ChangeFrequency(1)
+  } else if (frequency_data.value?.frequency_type == 'Daily') {
+    radio.value = 2
+    DailyTime.value = frequency_data.value?.daily_time
+    TimeZoneDailySelect.value = frequency_data.value?.daily_time_zone
+    ChangeFrequency(2)
+  } else if (frequency_data.value?.frequency_type == 'Weekly') {
+    radio.value = 3
+    WeeklyDay.value = frequency_data.value?.weekly_week
+    WeeklyTime.value = frequency_data.value?.weekly_time
+    TimeZoneWeeklySelect.value = frequency_data.value?.weekly_time_zone
+    ChangeFrequency(3)
+  } else {
+    radio.value = 0
+    ChangeFrequency(0)
+  }
+}
+
+// 更改Frequency时间
+const ChangeFrequency = (val: any) => {
+  FrequencyList.value = []
+  let str: any = ''
+  if (val == 1) {
+    isDaily.value = false
+    isWeekly.value = false
+    str = 'Instant notification for each update'
+    FrequencyList.value.push(str)
+    savesubscribeobj.frequency_type = 'Instant'
+    savesubscribeobj.frequency_display = str
+  } else if (val == 2) {
+    isDaily.value = true
+    isWeekly.value = false
+    str = 'Daily, ' + DailyTime.value + ', ' + TimeZoneDailySelect.value
+    if (DailyTime.value != '' && TimeZoneDailySelect.value !== '') {
+      FrequencyList.value.push(str)
+    }
+    savesubscribeobj.frequency_type = 'Daily'
+    savesubscribeobj.daily_time = DailyTime.value
+    savesubscribeobj.daily_time_zone = TimeZoneDailySelect.value
+    savesubscribeobj.frequency_display = str
+  } else if (val == 3) {
+    isDaily.value = false
+    isWeekly.value = true
+    str = 'Weekly, ' + WeeklyDay.value + ', ' + WeeklyTime.value + ', ' + TimeZoneWeeklySelect.value
+    if (WeeklyDay.value != '' && WeeklyTime.value != '' && TimeZoneWeeklySelect.value !== '') {
+      FrequencyList.value.push(str)
+    }
+    savesubscribeobj.frequency_type = 'Weekly'
+    if (WeeklyDay.value == 'Monday') {
+      savesubscribeobj.weekly_week = 1
+    } else if (WeeklyDay.value == 'Tuesday') {
+      savesubscribeobj.weekly_week = 2
+    } else if (WeeklyDay.value == 'Wednesday') {
+      savesubscribeobj.weekly_week = 3
+    } else if (WeeklyDay.value == 'Thursday') {
+      savesubscribeobj.weekly_week = 4
+    } else if (WeeklyDay.value == 'Friday') {
+      savesubscribeobj.weekly_week = 5
+    } else if (WeeklyDay.value == 'Saturday') {
+      savesubscribeobj.weekly_week = 6
+    } else if (WeeklyDay.value == 'Sunday') {
+      savesubscribeobj.weekly_week = 0
+    }
+    savesubscribeobj.weekly_time = WeeklyTime.value
+    savesubscribeobj.weekly_time_zone = TimeZoneWeeklySelect.value
+    savesubscribeobj.frequency_display = str
+  } else {
+    isDaily.value = false
+    isWeekly.value = false
+    DailyTime.value = ''
+    WeeklyTime.value = ''
+    WeeklyDay.value = ''
+    delete savesubscribeobj.frequency_type
+    delete savesubscribeobj.daily_time
+    delete savesubscribeobj.daily_time_zone
+    delete savesubscribeobj.weekly_week
+    delete savesubscribeobj.weekly_time
+    delete savesubscribeobj.weekly_time_zone
+  }
+  emits('ChangeFrequencyAdd', FrequencyList.value, savesubscribeobj)
+}
+const changeTime = (val: any) => {
+  if (val == 'Daily') {
+    ChangeFrequency(2)
+  } else {
+    ChangeFrequency(3)
+  }
+}
+
+// 删除 Frequency tag
+const handleCloseRadioFrequency = () => {
+  radio.value = 0
+  ChangeFrequency(0)
+}
+
+const user_type = localStorage.getItem('user_type')
+
+defineExpose({
+  handleCloseRadioFrequency,
+  FrequencyDataInit
+})
+</script>
+<template>
+  <div style="margin-top: 11px">
+    <el-radio-group v-model="radio" @change="ChangeFrequency">
+      <el-radio :value="1" v-if="user_type != null && user_type != 'customer'"
+        >Instant notification for each update</el-radio
+      >
+      <el-radio :value="2">
+        <div>Daily Summary</div>
+        <div class="Daily" v-if="isDaily">
+          <div class="Daily_left" style="margin-right: 8px">
+            Select Time
+            <div>
+              <el-time-select
+                v-model="DailyTime"
+                start="00:00"
+                step="00:30"
+                end="23:30"
+                prefix-icon=""
+                @change="changeTime('Daily')"
+                placeholder="Select Time"
+              ></el-time-select>
+            </div>
+          </div>
+          <div class="Daily_left">
+            Select Time Zone
+            <div>
+              <el-select
+                v-model="TimeZoneDailySelect"
+                placeholder="Select Time Zone"
+                @change="changeTime('Daily')"
+              >
+                <el-option
+                  v-for="item in TimeZone"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </div>
+          </div>
+        </div>
+      </el-radio>
+      <el-radio :value="3">
+        <div>Weekly Summary</div>
+        <div class="Daily" v-if="isWeekly">
+          <div class="Weekly_left">
+            Select Day
+            <div>
+              <el-select
+                v-model="WeeklyDay"
+                @change="changeTime('Weekly')"
+                placeholder="Select Day"
+              >
+                <el-option
+                  v-for="item in WeekDay"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </div>
+          </div>
+          <div class="Weekly_left" style="margin: 0 8px">
+            Select Time
+            <div>
+              <el-time-select
+                v-model="WeeklyTime"
+                @change="changeTime('Weekly')"
+                start="00:00"
+                step="00:30"
+                end="23:30"
+                prefix-icon=""
+                placeholder="Select time"
+              ></el-time-select>
+            </div>
+          </div>
+          <div class="Weekly_left">
+            Select Time Zone
+            <div>
+              <el-select
+                v-model="TimeZoneWeeklySelect"
+                placeholder="Select Time Zone"
+                @change="changeTime('Weekly')"
+              >
+                <el-option
+                  v-for="item in TimeZone"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </div>
+          </div>
+        </div>
+      </el-radio>
+    </el-radio-group>
+  </div>
+</template>
+<style lang="scss" scoped>
+:deep(.el-radio-group) {
+  display: block;
+}
+:deep(.el-radio) {
+  display: flex;
+  min-height: 32px;
+  border: 1px solid var(--color-select-border);
+  margin-bottom: 4px;
+  border-radius: 6px;
+  padding: 0 8px;
+  margin-right: 0;
+  height: fit-content;
+  line-height: 32px;
+  align-items: start;
+}
+:deep(.el-radio__input.is-checked + .el-radio__label) {
+  color: var(--color-neutral-1);
+}
+.Daily {
+  margin: 0 0 9px 0;
+  display: flex;
+}
+.Daily_left {
+  color: var(--color-neutral-2);
+  width: 50%;
+}
+.Weekly_left {
+  color: var(--color-neutral-2);
+  width: 33%;
+}
+:deep(.el-select__icon.el-icon svg) {
+  width: 1em !important;
+}
+:deep(.el-select__wrapper.is-filterable) {
+  padding-left: 7px;
+}
+:deep(.el-radio__inner) {
+  margin-top: 7px;
+}
+</style>

+ 92 - 0
src/components/AddRules/src/components/NotiMethods.vue

@@ -0,0 +1,92 @@
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+
+const checkMethodList = ref()
+checkMethodList.value = []
+let savesubscribeobj: any = {}
+const props = defineProps({
+  MethodsData: Object
+})
+
+const methods_data = ref(props.MethodsData)
+watch(
+  () => props.MethodsData,
+  (current) => {
+    methods_data.value = current
+    MethodsInit()
+  }
+)
+const emits = defineEmits(['ChangeMethodsAdd'])
+// 初始设置Methods
+const MethodsInit = () => {
+  if (methods_data.value?.method_display != undefined) {
+    if (methods_data.value?.method_display.indexOf('Email') != -1) {
+      checkMethodList.value.push('By Email')
+    }
+  }
+  if (methods_data.value?.method_display != undefined) {
+    if (methods_data.value?.method_display.indexOf('System Message') != -1) {
+      checkMethodList.value.push('By System Message')
+    }
+  }
+  changeMethod(checkMethodList.value)
+}
+
+// 选中Method
+const changeMethod = (val: any) => {
+  if (val.indexOf('By Email') != -1) {
+    savesubscribeobj.method_by_email = true
+  } else {
+    savesubscribeobj.method_by_email = false
+  }
+  if (val.indexOf('By System Message ') != -1) {
+    savesubscribeobj.method_by_message = true
+  } else {
+    savesubscribeobj.method_by_message = false
+  }
+  if (val.length != 0) {
+    savesubscribeobj.method_display = val.map((e: any) => e.replace('By ', '')).join(',')
+  } else {
+    savesubscribeobj.method_display = ''
+  }
+  emits('ChangeMethodsAdd', checkMethodList.value, savesubscribeobj)
+}
+const user_type = localStorage.getItem('user_type')
+</script>
+<template>
+  <div style="margin-top: 11px">
+    <div class="Method">
+      <el-checkbox-group v-model="checkMethodList" @change="changeMethod">
+        <el-checkbox
+          class="methodcheckbox"
+          value="By Email"
+          v-if="user_type != null && user_type != 'customer'"
+        >
+          <div>By Email</div>
+          <div class="methos_image"><img src="../images/illustration_email@2x.png" /></div>
+        </el-checkbox>
+        <el-checkbox class="methodcheckbox" value="By System Message">
+          <div>By System Message</div>
+          <div class="methos_image">
+            <img src="../images/illustration_system massage@2x.png" />
+          </div>
+        </el-checkbox>
+      </el-checkbox-group>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.methodcheckbox {
+  align-items: start;
+  height: fit-content;
+  width: 49%;
+  margin-right: 5px;
+  background-color: var(--color-dialog-header-bg);
+  border-radius: 6px;
+  padding: 11px 0 0 13px;
+}
+.methos_image {
+  margin: 9px 0;
+}
+</style>

+ 83 - 0
src/components/AddRules/src/components/RulesShipments.vue

@@ -0,0 +1,83 @@
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+
+interface OceanCheckboxItem {
+  value: string
+  label: string
+}
+interface Props {
+  CheckboxList: OceanCheckboxItem[]
+  Title: String
+  CheckedList: Array<''>
+}
+const props = defineProps<Props>()
+
+const OceanActive = ref(['OceanShipments'])
+const CheckedList = ref(props.CheckedList)
+const CheckboxList = ref(props.CheckboxList)
+watch(
+  () => props.CheckboxList,
+  (current) => {
+    CheckboxList.value = current
+  }
+)
+watch(
+  () => props.CheckedList,
+  (current) => {
+    CheckedList.value = current
+  }
+)
+
+const emit = defineEmits(['ChangeCheckRules'])
+const CheckChange = () => {
+  console.log(CheckedList.value)
+  emit('ChangeCheckRules', CheckedList.value)
+}
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="OceanShipments">
+        <template #title>
+          <div class="Rules_Title OceanTitle">{{ props.Title }}</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="CheckedList">
+            <el-checkbox
+              v-for="item in CheckboxList"
+              :key="item.label"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+.oceanCheckbox {
+  max-height: 321px;
+  overflow-x: hidden;
+  margin-bottom: 8px;
+  overflow-y: scroll;
+}
+</style>

+ 196 - 0
src/components/AddRules/src/components/ShipmentRange.vue

@@ -0,0 +1,196 @@
+<script lang="ts" setup>
+import { ref, computed } from 'vue'
+
+const OceanActive = ref(['TransportMode', 'Time'])
+const TransportCheckedList = ref([])
+interface OceanItem {
+  label: string
+  value: string
+}
+const TransportList = ref<OceanItem[]>([])
+TransportList.value = [
+  {
+    label: 'Ocean',
+    value: 'Ocean'
+  },
+  {
+    label: 'Air',
+    value: 'Air'
+  },
+  {
+    label: 'Road',
+    value: 'Road'
+  }
+]
+
+const TimeChecked = ref()
+
+const ETDTime = ref('')
+const ETATime = ref('')
+
+const clampedETDValue = computed(() => {
+  if (ETDTime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETDTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+const clampedETAValue = computed(() => {
+  if (ETATime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETATime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+
+let Transportstr: any = ''
+let Timestr: any = ''
+const emit = defineEmits(['ChangeCheckRules', 'ChangeCheckTimeRules'])
+const CheckChange = (val: any) => {
+  if (val != '') {
+    Transportstr = 'Transport Mode: ' + val
+  } else {
+    Transportstr = ''
+  }
+  emit('ChangeCheckRules', Transportstr)
+}
+
+// 输入ETD、ETA
+const changeTime = (val: any) => {
+  if (val == 1) {
+    Timestr = 'ETD within ' + clampedETDValue.value + ' Day(s)'
+  } else if (val == 2) {
+    Timestr = 'ETA within ' + clampedETAValue.value + ' Day(s)'
+  } else {
+    Timestr = ''
+  }
+  emit('ChangeCheckTimeRules', Timestr)
+}
+
+const handleCloseCreateRule = (val: any) => {
+  if (val.indexOf('ETD') != -1 || val.indexOf('ETA') != -1) {
+    TimeChecked.value = 0
+    ETDTime.value = ''
+    ETATime.value = ''
+  } else if (val.indexOf('Transport') != -1) {
+    TransportCheckedList.value = []
+  }
+}
+
+defineExpose({
+  handleCloseCreateRule
+})
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="TransportMode">
+        <template #title>
+          <div class="Rules_Title OceanTitle">Transport Mode</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="TransportCheckedList">
+            <el-checkbox
+              v-for="item in TransportList"
+              :key="item.label"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+      <el-collapse-item name="Time">
+        <template #title>
+          <div class="Rules_Title OceanTitle">Time</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-radio-group v-model="TimeChecked" @change="changeTime">
+            <el-radio value="1">
+              <div class="flex">
+                ETD within
+                <el-input
+                  @input="changeTime('1')"
+                  v-model="ETDTime"
+                  :value="clampedETDValue"
+                  class="input-with-select"
+                >
+                </el-input>
+                <div class="Days">Day(s)</div>
+              </div>
+            </el-radio>
+            <el-radio value="2">
+              <div class="flex">
+                ETA within
+                <el-input
+                  @input="changeTime('2')"
+                  v-model="ETATime"
+                  :value="clampedETAValue"
+                  class="input-with-select"
+                >
+                </el-input>
+                <div class="Days">Day(s)</div>
+              </div>
+            </el-radio>
+          </el-radio-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+.oceanCheckbox {
+  max-height: 321px;
+  overflow-x: hidden;
+  margin-bottom: 8px;
+  overflow-y: scroll;
+}
+
+.input-with-select {
+  width: 96px;
+  border-radius: 6px 0 0 6px;
+}
+:deep(.el-input__wrapper) {
+  border-radius: 6px 0 0 6px;
+  opacity: 0.8;
+  height: 28px;
+}
+.flex {
+  display: flex;
+  align-items: center;
+}
+.Days {
+  width: 64px;
+  border: 1px solid var(--color-select-border);
+  border-radius: 0 6px 6px 0;
+  background-color: var(--color-drawer-body-bg);
+  border-left: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  opacity: 0.8;
+  height: 28px;
+}
+:deep(.el-radio) {
+  background-color: var(--color-drawer-body-bg);
+}
+</style>

BIN
src/components/AddRules/src/images/icon_collapse.png


BIN
src/components/AddRules/src/images/icon_expand.png


BIN
src/components/AddRules/src/images/illustration_email@2x.png


BIN
src/components/AddRules/src/images/illustration_system massage@2x.png


BIN
src/components/AddRules/src/images/submit_successful.png


+ 1 - 0
src/components/CreateAddRules/index.ts

@@ -0,0 +1 @@
+export { default } from './src/CreateAddRules.vue'

+ 1215 - 0
src/components/CreateAddRules/src/CreateAddRules.vue

@@ -0,0 +1,1215 @@
+<script lang="ts" setup>
+import { ref, watch, onMounted } from 'vue'
+import RulesShipments from './components/RulesShipments.vue'
+import AddedrluesTag from './components/AddedrluesTag.vue'
+import DelayedType from './components/DelayedType.vue'
+import ETDShipments from './components/ETDShipments.vue'
+import NotiFrequency from './components/NotiFrequency.vue'
+import ShipmentRange from './components/ShipmentRange.vue'
+import NotiMethods from './components/NotiMethods.vue'
+import submitsucessful from './images/submit_successful.png'
+interface CheckboxItem {
+  value: string
+  label: string
+}
+
+interface Props {
+  SystemList: Object
+  TitleType: String
+}
+const MilestoneOceanListInit = ref<CheckboxItem[]>([])
+const MilestoneOceanListChecked = ref()
+const MilestoneAirListInit = ref<CheckboxItem[]>([])
+const MilestoneAirListChecked = ref()
+const ContainerOceanListInit = ref<CheckboxItem[]>([])
+const ContainerOceanListChecked = ref()
+const props = defineProps<Props>()
+let savesubscribeobj: any = {}
+const SystemList = ref(props.SystemList)
+const RulesActive = ref([
+  'ShipmentRange',
+  'SelectMilestone',
+  'NotificationFrequency',
+  'NotificationMethod'
+])
+const OceanCheckList = ref()
+const AirCheckList = ref()
+const ContainerOceanList = ref()
+const IsFirstActive = ref(true)
+const IsTwoActive = ref(true)
+const IsThreeActive = ref(true)
+const IsFourActive = ref(true)
+const UnableSaveVisible = ref(false)
+const SaveedVisible = ref(false)
+const DelayedDeparturedList = ref()
+const DelayedAirdList = ref()
+const ETDOceanList = ref()
+const ETDAirList = ref()
+const FrequencyDataMil = ref()
+const FrequencyDataCon = ref()
+const FrequencyDataDep = ref()
+const FrequencyDataETD = ref()
+const MethodsDataMil = ref()
+const MethodsDataCon = ref()
+const MethodsDataDep = ref()
+const MethodsDataETD = ref()
+const DelayedDataInit = ref()
+const DelayedDataInitAir = ref()
+const OceanETDInit = ref()
+const AirETDInit = ref()
+
+watch(
+  () => props.SystemList,
+  (current) => {
+    SystemList.value = current
+    FrequencyDataMil.value = current['Milestone_Update']
+    MethodsDataMil.value = current['Milestone_Update']
+    FrequencyDataCon.value = current['Container_Status_Update']
+    MethodsDataCon.value = current['Container_Status_Update']
+    FrequencyDataDep.value = current['Departure/Arrival_Delay']
+    MethodsDataDep.value = current['Departure/Arrival_Delay']
+    FrequencyDataETD.value = current['ETD/ETA_Change']
+    MethodsDataETD.value = current['ETD/ETA_Change']
+    Initdata(current)
+  }
+)
+
+const MonitoringList = ref()
+const getInitMonitoring = () => {
+  $api
+    .MonitoringInit({})
+    .then((res: any) => {
+      if (res.code === 200) {
+        MonitoringList.value = res.data
+        MilestoneOceanListInit.value = res.data.OceanCheckBoxList
+        MilestoneAirListInit.value = res.data.AirCheckBoxList
+        ContainerOceanListInit.value = res.data.CtnrCheckBoxList
+      }
+    })
+    .finally(() => {})
+}
+onMounted(() => {
+  getInitMonitoring()
+})
+// 初始赋值
+const Initdata = (val: any) => {
+  console.log(val)
+  MilestoneOceanListInit.value = val.Milestone_Update.OceanCheckBoxList
+  MilestoneOceanListChecked.value = val.Milestone_Update.OceanCheckedList
+  OceanCheckList.value = val.Milestone_Update.OceanCheckedList
+  MilestoneAirListInit.value = val.Milestone_Update.AirCheckBoxList
+  MilestoneAirListChecked.value = val.Milestone_Update.AirCheckedList
+  AirCheckList.value = val.Milestone_Update.AirCheckedList
+  ContainerOceanListInit.value = val.Container_Status_Update.CtnrCheckBoxList
+  ContainerOceanListChecked.value = val.Container_Status_Update.CtnrCheckedList
+  ContainerOceanList.value = val.Container_Status_Update.CtnrCheckedList
+  let OceanObj: any = {}
+  OceanObj.atd_etd = val['Departure/Arrival_Delay'].ocean_atd_sub_etd
+  OceanObj.atd_etd_unit = val['Departure/Arrival_Delay'].ocean_atd_sub_etd_unit
+  OceanObj.ata_eta = val['Departure/Arrival_Delay'].ocean_ata_sub_eta
+  OceanObj.ata_eta_unit = val['Departure/Arrival_Delay'].ocean_ata_sub_eta_unit
+  DelayedDataInit.value = OceanObj
+  let AirObj: any = {}
+  AirObj.atd_etd = val['Departure/Arrival_Delay'].air_atd_sub_etd
+  AirObj.atd_etd_unit = val['Departure/Arrival_Delay'].air_atd_sub_etd_unit
+  AirObj.ata_eta = val['Departure/Arrival_Delay'].air_ata_sub_eta
+  AirObj.ata_eta_unit = val['Departure/Arrival_Delay'].air_ata_sub_eta_unit
+  DelayedDataInitAir.value = AirObj
+  let OceanChange: any = {}
+  OceanChange.ETDradio = val['ETD/ETA_Change'].ocean_etd_change
+  OceanChange.etd_old_sub_new = val['ETD/ETA_Change'].ocean_etd_old_sub_new
+  OceanChange.etd_old_sub_new_unit = val['ETD/ETA_Change'].ocean_etd_old_sub_new_unit
+  OceanChange.ETAradio = val['ETD/ETA_Change'].ocean_eta_change
+  OceanChange.eta_old_sub_new = val['ETD/ETA_Change'].ocean_eta_old_sub_new
+  OceanChange.eta_old_sub_new_unit = val['ETD/ETA_Change'].ocean_eta_old_sub_new_unit
+  OceanETDInit.value = OceanChange
+  let AirChange: any = {}
+  AirChange.ETDradio = val['ETD/ETA_Change'].air_etd_change
+  AirChange.etd_old_sub_new = val['ETD/ETA_Change'].air_etd_old_sub_new
+  AirChange.etd_old_sub_new_unit = val['ETD/ETA_Change'].air_etd_old_sub_new_unit
+  AirChange.ETAradio = val['ETD/ETA_Change'].air_eta_change
+  AirChange.eta_old_sub_new = val['ETD/ETA_Change'].air_eta_old_sub_new
+  AirChange.eta_old_sub_new_unit = val['ETD/ETA_Change'].air_eta_old_sub_new_unit
+  AirETDInit.value = AirChange
+}
+
+// 给tag list赋值
+const ChangeCheckOceanRules = (val: any) => {
+  OceanCheckList.value = val
+}
+const ChangeContainerRules = (val: any) => {
+  ContainerOceanList.value = val
+}
+// delayed赋值
+const ChangeDeayedRules = (val: any) => {
+  DelayedDeparturedList.value = []
+  if (val.Departure != '') {
+    DelayedDeparturedList.value.push(val.Departure)
+    savesubscribeobj.ocean_atd_sub_etd = val.Departure.split(' ')[3]
+    savesubscribeobj.ocean_atd_sub_etd_unit = val.Departure.split(' ')[4]
+  } else {
+    delete savesubscribeobj.ocean_atd_sub_etd
+    delete savesubscribeobj.ocean_atd_sub_etd_unit
+  }
+  if (val.Arrival != '') {
+    DelayedDeparturedList.value.push(val.Arrival)
+    savesubscribeobj.ocean_ata_sub_eta = val.Arrival.split(' ')[4]
+    savesubscribeobj.ocean_ata_sub_eta_unit = val.Arrival.split(' ')[5]
+  } else {
+    delete savesubscribeobj.ocean_ata_sub_eta
+    delete savesubscribeobj.ocean_ata_sub_eta_unit
+  }
+}
+const ChangeAirRules = (val: any) => {
+  DelayedAirdList.value = []
+  if (val.Departure != '') {
+    DelayedAirdList.value.push(val.Departure)
+    savesubscribeobj.air_atd_sub_etd = val.Departure.split(' ')[3]
+    savesubscribeobj.air_atd_sub_etd_unit = val.Departure.split(' ')[4]
+  } else {
+    delete savesubscribeobj.air_atd_sub_etd
+    delete savesubscribeobj.air_atd_sub_etd_unit
+  }
+  if (val.Arrival != '') {
+    DelayedAirdList.value.push(val.Arrival)
+    savesubscribeobj.air_ata_sub_eta = val.Arrival.split(' ')[4]
+    savesubscribeobj.air_ata_sub_eta_unit = val.Arrival.split(' ')[5]
+  } else {
+    delete savesubscribeobj.air_ata_sub_eta
+    delete savesubscribeobj.air_ata_sub_eta_unit
+  }
+}
+const ChangeCheckAirRules = (val: any) => {
+  AirCheckList.value = val
+}
+
+//选择create new rules
+const createListMilestone = ref()
+const createListContainer = ref()
+const createListDeparture = ref()
+const createListETDChange = ref()
+
+let createObj: any = {
+  Transportstr: '',
+  Timestr: ''
+}
+const changecheckCreateRulesMilestone = (val: any, value: any) => {
+  createListMilestone.value = []
+  createObj.Transportstr = val
+  if (createObj.Transportstr != '') {
+    createListMilestone.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListMilestone.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_transport_mode = value
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+const ChangeCheckTimeRulesMilestone = (val: any, time: any) => {
+  createListMilestone.value = []
+  createObj.Timestr = val
+  if (val.includes('ETD')) {
+    savesubscribeobj.shipment_etd_limit = time
+    savesubscribeobj.shipment_eta_limit = ''
+  }
+  if (val.includes('ETA')) {
+    savesubscribeobj.shipment_eta_limit = time
+    savesubscribeobj.shipment_etd_limit = ''
+  }
+  if (createObj.Transportstr != '') {
+    createListMilestone.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListMilestone.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+const changecheckCreateRulesContainer = (val: any, value: any) => {
+  createListContainer.value = []
+  createObj.Transportstr = val
+  if (createObj.Transportstr != '') {
+    createListContainer.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListContainer.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_transport_mode = value
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+const ChangeCheckTimeRulesContainer = (val: any, time: any) => {
+  createListContainer.value = []
+  createObj.Timestr = val
+  if (val.includes('ETD')) {
+    savesubscribeobj.shipment_etd_limit = time
+    savesubscribeobj.shipment_eta_limit = ''
+  }
+  if (val.includes('ETA')) {
+    savesubscribeobj.shipment_eta_limit = time
+    savesubscribeobj.shipment_etd_limit = ''
+  }
+  if (createObj.Transportstr != '') {
+    createListContainer.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListContainer.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+const changecheckCreateRulesDeparture = (val: any, value: any) => {
+  createListDeparture.value = []
+  createObj.Transportstr = val
+  if (createObj.Transportstr != '') {
+    createListDeparture.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListDeparture.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_transport_mode = value
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+const ChangeCheckTimeRulesDeparture = (val: any, time: any) => {
+  createListDeparture.value = []
+  createObj.Timestr = val
+  if (val.includes('ETD')) {
+    savesubscribeobj.shipment_etd_limit = time
+    savesubscribeobj.shipment_eta_limit = ''
+  }
+  if (val.includes('ETA')) {
+    savesubscribeobj.shipment_eta_limit = time
+    savesubscribeobj.shipment_etd_limit = ''
+  }
+  if (createObj.Transportstr != '') {
+    createListDeparture.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListDeparture.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+const changecheckCreateRulesETDChange = (val: any, value: any) => {
+  createListETDChange.value = []
+  createObj.Transportstr = val
+  if (createObj.Transportstr != '') {
+    createListETDChange.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListETDChange.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_transport_mode = value
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+const ChangeCheckTimeRulesETDChange = (val: any, time: any) => {
+  createListETDChange.value = []
+  createObj.Timestr = val
+  if (val.includes('ETD')) {
+    savesubscribeobj.shipment_etd_limit = time
+    savesubscribeobj.shipment_eta_limit = ''
+  }
+  if (val.includes('ETA')) {
+    savesubscribeobj.shipment_eta_limit = time
+    savesubscribeobj.shipment_etd_limit = ''
+  }
+  if (createObj.Transportstr != '') {
+    createListETDChange.value.push(createObj.Transportstr)
+  }
+  if (createObj.Timestr != '') {
+    createListETDChange.value.push(createObj.Timestr)
+  }
+  savesubscribeobj.shipment_details = createListMilestone.value.join(';\n')
+}
+// 删除create tag
+const ShipmentRangeRef = ref()
+const handleCloseCreateRule = (val: any) => {
+  if (val.indexOf('ETD') != -1 || val.indexOf('ETA') != -1) {
+    createObj.Timestr = ''
+  } else if (val.indexOf('Transport') != -1) {
+    createObj.Transportstr = ''
+  }
+  ShipmentRangeRef.value.handleCloseCreateRule(val)
+}
+// 更改Frequency时间
+const MilFrequencyList = ref([])
+const ChangeMilFrequency = (val: any, type: any) => {
+  MilFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ConFrequencyList = ref([])
+const ChangeConFrequency = (val: any, type: any) => {
+  ConFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const DepFrequencyList = ref([])
+const ChangeDepFrequency = (val: any, type: any) => {
+  DepFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ETDFrequencyList = ref([])
+const ChangeETDFrequency = (val: any, type: any) => {
+  ETDFrequencyList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+
+// 删除 Frequency tag
+const NotiFrequencyDeleteMil = ref()
+const NotiFrequencyDeleteCon = ref()
+const NotiFrequencyDeleteDep = ref()
+const NotiFrequencyDeleteETD = ref()
+const handleCloseRadio = (val: any) => {
+  if (val == 'Mil') {
+    NotiFrequencyDeleteMil.value.handleCloseRadioFrequency()
+  } else if (val == 'Con') {
+    NotiFrequencyDeleteCon.value.handleCloseRadioFrequency()
+  } else if (val == 'Dep') {
+    NotiFrequencyDeleteDep.value.handleCloseRadioFrequency()
+  } else {
+    NotiFrequencyDeleteETD.value.handleCloseRadioFrequency()
+  }
+}
+
+// methods 切换
+const MilMethodsList = ref([])
+const ChangeMethodsAddMil = (val: any, type: any) => {
+  MilMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ConMethodsList = ref([])
+const ChangeMethodsAddCon = (val: any, type: any) => {
+  ConMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const DepMethodsList = ref([])
+const ChangeMethodsAddDep = (val: any, type: any) => {
+  DepMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const ETDMethodsList = ref([])
+const ChangeMethodsAddETD = (val: any, type: any) => {
+  ETDMethodsList.value = val
+  savesubscribeobj = { ...savesubscribeobj, ...type }
+}
+const OceanDelayed = ref()
+const AirDelayed = ref()
+
+const handleCloseDelayed = (val: any) => {
+  OceanDelayed.value.closeDelayed(val)
+}
+const handleCloseAirDelayed = (val: any) => {
+  AirDelayed.value.closeDelayed(val)
+}
+// ETD Change
+const ChangeETDOceanRules = (val: any) => {
+  ETDOceanList.value = []
+  if (val.ETD != '') {
+    if (val.ETD.indexOf('≥') != -1) {
+      savesubscribeobj.ocean_etd_change = false
+      savesubscribeobj.ocean_etd_old_sub_new = val.ETD.split(' ')[6]
+      savesubscribeobj.ocean_etd_old_sub_new_unit = val.ETD.split(' ')[7]
+    } else {
+      savesubscribeobj.ocean_etd_change = true
+    }
+    ETDOceanList.value.push(val.ETD)
+  } else {
+    delete savesubscribeobj.ocean_etd_change
+    delete savesubscribeobj.ocean_etd_old_sub_new
+    delete savesubscribeobj.ocean_etd_old_sub_new_unit
+  }
+  if (val.ETA != '') {
+    if (val.ETA.indexOf('≥') != -1) {
+      savesubscribeobj.ocean_eta_change = false
+      savesubscribeobj.ocean_eta_old_sub_new = val.ETA.split(' ')[6]
+      savesubscribeobj.ocean_eta_old_sub_new_unit = val.ETA.split(' ')[7]
+    } else {
+      savesubscribeobj.ocean_eta_change = true
+    }
+    ETDOceanList.value.push(val.ETA)
+  } else {
+    delete savesubscribeobj.ocean_eta_change
+    delete savesubscribeobj.ocean_eta_old_sub_new
+    delete savesubscribeobj.ocean_eta_old_sub_new_unit
+  }
+}
+// 删除ETD tag
+const OceanETD = ref()
+const closeOceanETD = (val: any) => {
+  OceanETD.value.closeETD(val)
+}
+// ETA Change
+const ChangeETDAirRules = (val: any) => {
+  ETDAirList.value = []
+  if (val.ETD != '') {
+    if (val.ETD.indexOf('≥') != -1) {
+      savesubscribeobj.air_etd_change = false
+      savesubscribeobj.air_etd_old_sub_new = val.ETD.split(' ')[6]
+      savesubscribeobj.air_etd_old_sub_new_unit = val.ETD.split(' ')[7]
+    } else {
+      savesubscribeobj.air_etd_change = true
+    }
+    ETDAirList.value.push(val.ETD)
+  } else {
+    delete savesubscribeobj.air_etd_change
+    delete savesubscribeobj.air_etd_old_sub_new
+    delete savesubscribeobj.air_etd_old_sub_new_unit
+  }
+  if (val.ETA != '') {
+    if (val.ETA.indexOf('≥') != -1) {
+      savesubscribeobj.air_eta_change = false
+      savesubscribeobj.air_eta_old_sub_new = val.ETA.split(' ')[6]
+      savesubscribeobj.air_eta_old_sub_new_unit = val.ETA.split(' ')[7]
+    } else {
+      savesubscribeobj.air_eta_change = true
+    }
+    ETDAirList.value.push(val.ETA)
+  } else {
+    delete savesubscribeobj.air_eta_change
+    delete savesubscribeobj.air_eta_old_sub_new
+    delete savesubscribeobj.air_eta_old_sub_new_unit
+  }
+}
+
+// 删除ETA tag
+const AirETD = ref()
+const closeAirETD = (val: any) => {
+  AirETD.value.closeETD(val)
+}
+
+const emits = defineEmits(['SavedAddedRules'])
+// 不保存修改的折叠面板
+
+// 保存subscribe配置
+const missingmessage = ref('')
+// 保存成功调用接口
+const SaveSuceessful = () => {
+  $api
+    .MonitoringSave({
+      ...savesubscribeobj
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        SaveedVisible.value = true
+        setTimeout(() => {
+          SaveedVisible.value = false
+          emits('SavedAddedRules', res.data.addedRules, savesubscribeobj.rules_type)
+        }, 3000)
+      }
+    })
+}
+const Savesubscribe = () => {
+  missingmessage.value = ''
+  let str = ''
+  if (props.TitleType == 'Milestone') {
+    savesubscribeobj.rules_type = 'Milestone_Update'
+    if (
+      OceanCheckList.value == undefined ||
+      AirCheckList.value == undefined ||
+      MilFrequencyList.value.length == 0 ||
+      MilMethodsList.value.length == 0 ||
+      createObj.Transportstr == '' ||
+      createObj.Timestr == ''
+    ) {
+      console.log(createObj)
+      if (createObj.Transportstr == '') {
+        missingmessage.value += 'Transport Mode, '
+      }
+      if (createObj.Timestr == '') {
+        missingmessage.value += 'Time, '
+      }
+      if (OceanCheckList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (AirCheckList.value == undefined) {
+        missingmessage.value += 'Air Shipments, '
+      }
+      if (MilFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (MilMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      savesubscribeobj.ocean_milestone = OceanCheckList.value
+      savesubscribeobj.air_milestone = AirCheckList.value
+      str =
+        'Ocean Milestones: ' +
+        OceanCheckList.value.join(',') +
+        ';\nAir Milestones: ' +
+        AirCheckList.value.join(',') +
+        ';'
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  } else if (props.TitleType == 'Container') {
+    savesubscribeobj.rules_type = 'Container_Status_Update'
+    if (
+      ContainerOceanList.value == undefined ||
+      ConFrequencyList.value.length == 0 ||
+      ConMethodsList.value.length == 0
+    ) {
+      if (ContainerOceanList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (ConFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (ConMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      savesubscribeobj.ocean_ctnr_status = ContainerOceanList.value
+      str = 'Ocean Container: ' + ContainerOceanList.value.join(',')
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  } else if (props.TitleType == 'Departure') {
+    savesubscribeobj.rules_type = 'Departure/Arrival_Delay'
+    if (
+      DelayedDeparturedList.value == undefined ||
+      DelayedAirdList.value == undefined ||
+      DepFrequencyList.value.length == 0 ||
+      DepMethodsList.value.length == 0
+    ) {
+      if (DelayedDeparturedList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (DelayedAirdList.value == undefined) {
+        missingmessage.value += 'Air Shipments, '
+      }
+      if (DepFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (DepMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      str = DelayedDeparturedList.value.join(';\n') + ';\n' + DelayedAirdList.value.join(';\n')
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  } else {
+    savesubscribeobj.rules_type = 'ETD/ETA_Change'
+    if (
+      ETDOceanList.value == undefined ||
+      ETDOceanList.value.length == 0 ||
+      ETDAirList.value == undefined ||
+      ETDAirList.value.length == 0 ||
+      ETDFrequencyList.value.length == 0 ||
+      ETDMethodsList.value.length == 0
+    ) {
+      if (ETDOceanList.value == undefined) {
+        missingmessage.value += 'Ocean Shipments, '
+      }
+      if (ETDAirList.value == undefined) {
+        missingmessage.value += 'Air Shipments, '
+      }
+      if (ETDFrequencyList.value.length == 0) {
+        missingmessage.value += 'Notification Frequency, '
+      }
+      if (ETDMethodsList.value.length == 0) {
+        missingmessage.value += 'Notification Method, '
+      }
+      missingmessage.value = missingmessage.value.substring(0, missingmessage.value.length - 2)
+      UnableSaveVisible.value = true
+    } else {
+      str = ETDOceanList.value.join(';\n') + ';\n' + ETDAirList.value.join(';\n')
+      savesubscribeobj.event_details = str
+      SaveSuceessful()
+    }
+  }
+}
+
+// 删除表格数据后清空所有数据
+const clearData = (val: any) => {
+  if (val == 'Milestone') {
+    OceanCheckList.value = []
+    AirCheckList.value = []
+    MilFrequencyList.value = []
+    MilMethodsList.value = []
+    MilestoneOceanListChecked.value = []
+    MilestoneAirListChecked.value = []
+    createListMilestone.value = []
+  } else if (val == 'Container') {
+    ContainerOceanList.value = []
+    createListContainer.value = []
+    ConFrequencyList.value = []
+    ConMethodsList.value = []
+    ContainerOceanListChecked.value = []
+  } else if (val == 'Departure') {
+    DelayedDeparturedList.value = []
+    DelayedAirdList.value = []
+    DepFrequencyList.value = []
+    DepMethodsList.value = []
+    createListDeparture.value = []
+  } else {
+    ETDOceanList.value = []
+    ETDAirList.value = []
+    ETDFrequencyList.value = []
+    ETDMethodsList.value = []
+    createListETDChange.value = []
+  }
+}
+
+defineExpose({
+  clearData,
+  Savesubscribe
+})
+</script>
+<template>
+  <div class="Rules_flex">
+    <div class="Rules_left">
+      <div class="Rules_collapse">
+        <el-collapse v-model="RulesActive" @change="IsFourActive = !IsFourActive">
+          <el-collapse-item name="ShipmentRange">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFourActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Shipment Range
+              </div>
+            </template>
+            <div>
+              <ShipmentRange
+                ref="ShipmentRangeRef"
+                v-if="props.TitleType == 'Milestone'"
+                @ChangeCheckRules="changecheckCreateRulesMilestone"
+                @ChangeCheckTimeRules="ChangeCheckTimeRulesMilestone"
+              ></ShipmentRange>
+              <ShipmentRange
+                ref="ShipmentRangeRef"
+                v-if="props.TitleType == 'Container'"
+                @ChangeCheckRules="changecheckCreateRulesContainer"
+                @ChangeCheckTimeRules="ChangeCheckTimeRulesContainer"
+              ></ShipmentRange>
+              <ShipmentRange
+                ref="ShipmentRangeRef"
+                v-if="props.TitleType == 'Departure'"
+                @ChangeCheckRules="changecheckCreateRulesDeparture"
+                @ChangeCheckTimeRules="ChangeCheckTimeRulesDeparture"
+              ></ShipmentRange>
+              <ShipmentRange
+                ref="ShipmentRangeRef"
+                v-if="props.TitleType == 'ETDChange'"
+                @ChangeCheckRules="changecheckCreateRulesETDChange"
+                @ChangeCheckTimeRules="ChangeCheckTimeRulesETDChange"
+              ></ShipmentRange>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'Milestone'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Milestone
+              </div>
+            </template>
+            <div>
+              <RulesShipments
+                Title="Ocean shipments"
+                @ChangeCheckRules="ChangeCheckOceanRules"
+                :CheckboxList="MilestoneOceanListInit"
+                :CheckedList="MilestoneOceanListChecked"
+              ></RulesShipments>
+            </div>
+            <div>
+              <RulesShipments
+                Title="Air shipments"
+                @ChangeCheckRules="ChangeCheckAirRules"
+                :CheckboxList="MilestoneAirListInit"
+                :CheckedList="MilestoneAirListChecked"
+              ></RulesShipments>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'Container'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Container Status
+              </div>
+            </template>
+            <div>
+              <RulesShipments
+                Title="Ocean shipments"
+                @ChangeCheckRules="ChangeContainerRules"
+                :CheckboxList="ContainerOceanListInit"
+                :CheckedList="ContainerOceanListChecked"
+              ></RulesShipments>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'Departure'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Delayed Type
+              </div>
+            </template>
+            <div>
+              <DelayedType
+                Title="Ocean shipments"
+                ref="OceanDelayed"
+                :DelayedData="DelayedDataInit"
+                @ChangeCheckRules="ChangeDeayedRules"
+              ></DelayedType>
+            </div>
+            <div>
+              <DelayedType
+                Title="Air shipments"
+                ref="AirDelayed"
+                :DelayedData="DelayedDataInitAir"
+                @ChangeCheckRules="ChangeAirRules"
+              ></DelayedType>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          v-model="RulesActive"
+          @change="IsFirstActive = !IsFirstActive"
+          v-if="props.TitleType == 'ETDChange'"
+        >
+          <el-collapse-item name="SelectMilestone">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsFirstActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Select Delayed Type
+              </div>
+            </template>
+            <div>
+              <ETDShipments
+                Title="Ocean shipments"
+                ref="OceanETD"
+                :ETDData="OceanETDInit"
+                @ChangeCheckRules="ChangeETDOceanRules"
+              ></ETDShipments>
+            </div>
+            <div>
+              <ETDShipments
+                Title="Air shipments"
+                ref="AirETD"
+                :ETDData="AirETDInit"
+                @ChangeCheckRules="ChangeETDAirRules"
+              ></ETDShipments>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          style="margin-top: 17px; margin-right: 16px"
+          v-model="RulesActive"
+          @change="IsTwoActive = !IsTwoActive"
+        >
+          <el-collapse-item name="NotificationFrequency">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsTwoActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Notification Frequency
+              </div>
+            </template>
+            <NotiFrequency
+              v-if="props.TitleType == 'Milestone'"
+              ref="NotiFrequencyDeleteMil"
+              :FrequencyData="FrequencyDataMil"
+              @ChangeFrequencyAdd="ChangeMilFrequency"
+            ></NotiFrequency>
+            <NotiFrequency
+              v-if="props.TitleType == 'Container'"
+              ref="NotiFrequencyDeleteCon"
+              :FrequencyData="FrequencyDataCon"
+              @ChangeFrequencyAdd="ChangeConFrequency"
+            ></NotiFrequency>
+            <NotiFrequency
+              v-if="props.TitleType == 'Departure'"
+              ref="NotiFrequencyDeleteDep"
+              :FrequencyData="FrequencyDataDep"
+              @ChangeFrequencyAdd="ChangeDepFrequency"
+            ></NotiFrequency>
+            <NotiFrequency
+              v-if="props.TitleType == 'ETDChange'"
+              ref="NotiFrequencyDeleteETD"
+              :FrequencyData="FrequencyDataETD"
+              @ChangeFrequencyAdd="ChangeETDFrequency"
+            ></NotiFrequency>
+          </el-collapse-item>
+        </el-collapse>
+        <el-collapse
+          style="margin: 17px 0"
+          v-model="RulesActive"
+          @change="IsThreeActive = !IsThreeActive"
+        >
+          <el-collapse-item name="NotificationMethod">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon">
+                  <svg class="iconfont" aria-hidden="true">
+                    <use
+                      :xlink:href="IsThreeActive ? '#icon-icon_dropdown_b' : '#icon-icon_up_b'"
+                    ></use>
+                  </svg>
+                </span>
+                <span class="stars_red">*</span>Notification Method
+              </div>
+            </template>
+            <NotiMethods
+              v-if="props.TitleType == 'Milestone'"
+              :MethodsData="MethodsDataMil"
+              @ChangeMethodsAdd="ChangeMethodsAddMil"
+            ></NotiMethods>
+            <NotiMethods
+              v-if="props.TitleType == 'Container'"
+              :MethodsData="MethodsDataCon"
+              @ChangeMethodsAdd="ChangeMethodsAddCon"
+            ></NotiMethods>
+            <NotiMethods
+              v-if="props.TitleType == 'Departure'"
+              :MethodsData="MethodsDataDep"
+              @ChangeMethodsAdd="ChangeMethodsAddDep"
+            ></NotiMethods>
+            <NotiMethods
+              v-if="props.TitleType == 'ETDChange'"
+              :MethodsData="MethodsDataETD"
+              @ChangeMethodsAdd="ChangeMethodsAddETD"
+            ></NotiMethods>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+    </div>
+    <div class="Rules_right">
+      <div class="right_Title">Added Rules</div>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="createListMilestone"
+        Title="Shipment Range"
+        @handleCloseRadio="handleCloseCreateRule"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Container'"
+        :CheckedList="createListContainer"
+        Title="Shipment Range"
+        @handleCloseRadio="handleCloseCreateRule"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Departure'"
+        :CheckedList="createListDeparture"
+        Title="Shipment Range"
+        @handleCloseRadio="handleCloseCreateRule"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'ETDChange'"
+        :CheckedList="createListETDChange"
+        Title="Shipment Range"
+        @handleCloseRadio="handleCloseCreateRule"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        :CheckedList="DelayedDeparturedList"
+        v-if="props.TitleType == 'Departure'"
+        @handleCloseRadio="handleCloseDelayed"
+        Title="Ocean Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="OceanCheckList"
+        Title="Ocean Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Container'"
+        :CheckedList="ContainerOceanList"
+        Title="Ocean Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        :CheckedList="ETDOceanList"
+        v-if="props.TitleType == 'ETDChange'"
+        Title="Ocean Shipments"
+        @handleCloseRadio="closeOceanETD"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        :CheckedList="ETDAirList"
+        v-if="props.TitleType == 'ETDChange'"
+        @handleCloseRadio="closeAirETD"
+        Title="Air Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        :CheckedList="DelayedAirdList"
+        v-if="props.TitleType == 'Departure'"
+        @handleCloseRadio="handleCloseAirDelayed"
+        Title="Air Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="AirCheckList"
+        Title="Air Shipments"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="MilFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('Mil')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Container'"
+        :CheckedList="ConFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('Con')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Departure'"
+        :CheckedList="DepFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('Dep')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'ETDChange'"
+        :CheckedList="ETDFrequencyList"
+        Title="Notification Frequency"
+        @handleCloseRadio="handleCloseRadio('ETD')"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Milestone'"
+        :CheckedList="MilMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Container'"
+        :CheckedList="ConMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'Departure'"
+        :CheckedList="DepMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+      <AddedrluesTag
+        v-if="props.TitleType == 'ETDChange'"
+        :CheckedList="ETDMethodsList"
+        Title="Notification Method"
+      ></AddedrluesTag>
+    </div>
+  </div>
+  <el-dialog v-model="UnableSaveVisible" width="480">
+    <div>{{ missingmessage }} missing.</div>
+    <div>Please complete all required fields.</div>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button
+          class="el-button--danger"
+          @click="UnableSaveVisible = false"
+          style="width: 100px"
+        >
+          OK
+        </el-button>
+      </div>
+    </template>
+    <template #header>
+      <div class="cancel_header">
+        <span class="iconfont_icon iconfont_warning">
+          <svg class="iconfont icon_danger" aria-hidden="true">
+            <use xlink:href="#icon-icon_fail_fill_b"></use>
+          </svg>
+        </span>
+        Unable to Save
+      </div>
+    </template>
+  </el-dialog>
+  <!-- 保存成功 -->
+  <el-dialog v-model="SaveedVisible" width="320" style="height: 212px">
+    <div style="text-align: center"><el-image :src="submitsucessful" /></div>
+    <div style="text-align: center; margin-top: 20px">Saved successfully</div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.Rules_flex {
+  padding: 0 0 0 8px;
+  display: flex;
+}
+.Rules_left {
+  width: 60%;
+  border-right: 1px solid var(--color-user-config-title-bottom-border);
+}
+.Rules_right {
+  width: 40%;
+  background-color: var(--more-filters-background-color);
+}
+.right_Title {
+  color: var(--color-neutral-1);
+  padding: 9px 8px;
+  font-size: 18px;
+  font-weight: 700;
+  border-bottom: 1px solid var(--color-user-config-title-bottom-border);
+  background-color: var(--color-drawer-body-bg);
+}
+.iconfont {
+  width: 16px;
+  height: 16px;
+  margin-right: 8px;
+}
+.icon_danger {
+  fill: var(--color-btn-danger-bg);
+}
+:deep(.Rules_Title) {
+  color: var(--color-neutral-1);
+  font-size: 14px;
+  font-weight: 700;
+  height: 22px;
+  display: flex;
+  align-items: center;
+}
+.stars_red {
+  color: var(--color-danger);
+}
+:deep(.el-collapse-item__header) {
+  height: 25px !important;
+  margin: 14px 0 0 0 !important;
+  border: none !important;
+  padding: 0 !important;
+}
+:deep(.el-collapse-item__header):hover {
+  background-color: #fff !important;
+  border: none !important;
+}
+:deep(.el-collapse-item__arrow) {
+  width: 0 !important;
+  height: 0 !important;
+}
+:deep(.el-icon svg) {
+  width: 0 !important;
+}
+:deep(.el-collapse-item__header.is-active) {
+  background-color: transparent !important;
+  border-color: transparent !important;
+}
+:deep(.el-collapse-item__arrow.is-active) {
+  transform: rotate(-180deg) !important;
+}
+:deep(.Ocean_collapse .el-collapse-item__arrow) {
+  width: 16px !important;
+  height: 16px !important;
+  background-image: url('../src/images/icon_expand.png') !important;
+  background-size: contain;
+  background-repeat: no-repeat;
+  transform: rotate(0);
+}
+:deep(.Ocean_collapse .el-collapse-item__header) {
+  background-color: #fff !important;
+  padding: 0 8px !important;
+  height: 40px !important;
+}
+:deep(.Ocean_collapse .el-collapse-item) {
+  background-color: var(--color-dialog-header-bg);
+  border-radius: 12px;
+}
+:deep(.Ocean_collapse .el-collapse-item__wrap) {
+  padding: 0 8px !important;
+}
+:deep(.Ocean_collapse .el-collapse-item__header.is-active) {
+  background-color: var(--color-dialog-header-bg) !important;
+}
+:deep(.el-checkbox__input.is-checked + .el-checkbox__label) {
+  color: var(--color-neutral-1);
+}
+.Rules_buttom {
+  padding: 8px;
+  border-top: 1px solid var(--color-border-1);
+}
+.rules_button {
+  width: 100px;
+  height: 40px;
+}
+.cancel_header {
+  font-size: 18px;
+  font-weight: 700;
+  color: var(--color-neutral-1);
+  display: flex;
+  align-items: center;
+}
+.icon_warning {
+  width: 22px;
+  height: 22px;
+  margin-right: 0;
+  fill: var(--color-btn-warning-bg);
+}
+.iconfont_warning {
+  display: flex;
+  align-items: center;
+}
+:deep(header.el-dialog__header) {
+  background-color: #fff;
+}
+:deep(footer.el-dialog__footer) {
+  border-top: none;
+}
+:deep(.el-collapse) {
+  margin-right: 8px;
+}
+
+:deep(.el-radio-group) {
+  display: block;
+}
+:deep(.el-radio) {
+  display: flex;
+  min-height: 32px;
+  border: 1px solid var(--color-select-border);
+  margin-bottom: 4px;
+  border-radius: 6px;
+  padding: 0 8px;
+  margin-right: 0;
+  height: fit-content;
+  line-height: 32px;
+  align-items: center;
+}
+:deep(.el-radio__input.is-checked + .el-radio__label) {
+  color: var(--color-neutral-1);
+}
+</style>

+ 78 - 0
src/components/CreateAddRules/src/components/AddedrluesTag.vue

@@ -0,0 +1,78 @@
+<script setup lang="ts" >
+import { ref, watch } from 'vue'
+const props = defineProps({
+  CheckedList: Array,
+  Title: String
+})
+const CheckedList = ref(props.CheckedList)
+watch(
+  () => props.CheckedList,
+  (current) => {
+    CheckedList.value = current
+  }
+)
+
+const emit = defineEmits(['handleCloseRadio'])
+
+const handleClose = (tag: any) => {
+  CheckedList.value?.splice(CheckedList.value.indexOf(tag), 1)
+  emit('handleCloseRadio', tag)
+}
+</script>
+
+<template>
+  <div>
+    <el-card class="Rules_Card">
+      <div class="Card_Title">{{ props.Title }}</div>
+      <div class="right_flex">
+        <el-tag
+          class="tag"
+          v-for="(tag, index) in CheckedList"
+          :key="index"
+          closable
+          @close="handleClose(tag)"
+        >
+          {{ tag }}
+        </el-tag>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.tag {
+  border-radius: 3px;
+  margin-bottom: 4px;
+  color: var(--color-neutral-1);
+  height: 32px;
+  width: fit-content;
+  font-weight: 400;
+  font-size: 14px;
+  background-color: var(--tag-bg-color) !important;
+  border-color: var(--tag-bg-color);
+}
+.tag:last-child {
+  margin-bottom: 0;
+}
+:deep(.el-card__body) {
+  padding: 8px !important;
+  max-height: 400px;
+  overflow-y: scroll;
+}
+:deep(.el-tag .el-tag__close svg) {
+  width: 16px !important;
+}
+.right_flex {
+  display: flex;
+  flex-direction: column;
+}
+.Rules_Card {
+  margin: 8px;
+}
+.Card_Title {
+  color: var(--color-neutral-1);
+  font-size: 14px;
+  font-weight: 600;
+  margin: 9px 0 11px 0;
+}
+</style>

+ 279 - 0
src/components/CreateAddRules/src/components/DelayedType.vue

@@ -0,0 +1,279 @@
+<script lang="ts" setup>
+import { ref, computed, watch } from 'vue'
+
+const props = defineProps({
+  Title: String,
+  DelayedData: Object
+})
+const delayed_data = ref(props.DelayedData)
+watch(
+  () => props.DelayedData,
+  (current) => {
+    delayed_data.value = current
+    DelayedInit()
+  }
+)
+
+const DelayedInit = () => {
+  let array2: any = []
+  OceanCheckedList.value = []
+  if (delayed_data.value?.atd_etd != '' && delayed_data.value?.atd_etd != undefined) {
+    isDeparture.value = true
+    DepartureTime.value = delayed_data.value?.atd_etd
+    DepartureSelect.value = delayed_data.value?.atd_etd_unit
+    OceanCheckedList.value.push('Departure Delayed')
+    array2.push('Departure')
+  }
+  if (delayed_data.value?.ata_eta != '' && delayed_data.value?.ata_eta != undefined) {
+    isArrival.value = true
+    ArrivalTime.value = delayed_data.value?.ata_eta
+    ArrivalSelect.value = delayed_data.value?.ata_eta_unit
+    OceanCheckedList.value.push('Arrival Delayed (ATA-ETA)')
+    array2.push('Arrival')
+  }
+  CheckChange(OceanCheckedList.value)
+  changedeparture(array2)
+}
+const isDeparture = ref(false)
+const isArrival = ref(false)
+const OceanActive = ref(['DelayedShipments'])
+const OceanCheckedList = ref()
+const DepartureTime = ref('')
+const DepartureSelect = ref('')
+const ArrivalTime = ref('')
+const ArrivalSelect = ref('')
+const emit = defineEmits(['ChangeCheckRules', 'closeDelayed'])
+const DepartureList = ref({
+  Departure: '',
+  Arrival: ''
+})
+let Departurestr: any = ''
+let Arrivalstr: any = ''
+
+const clampedValue = computed(() => {
+  if (DepartureTime.value == '') {
+    return 0
+  } else {
+    const numericValue = DepartureTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+const clampedArrivalValue = computed(() => {
+  if (ArrivalTime.value == '') {
+    return 0
+  } else {
+    const numericValue = ArrivalTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+
+const CheckChange = (val: any) => {
+  if (val.includes('Departure Delayed')) {
+    isDeparture.value = true
+    Departurestr = 'Departure Delayed' + ' ≥ ' + clampedValue.value + ' ' + DepartureSelect.value
+    if (DepartureSelect.value != '') {
+      DepartureList.value.Departure = Departurestr
+    }
+    if (val.includes('Arrival Delayed (ATA-ETA)')) {
+      isArrival.value = true
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    } else {
+      isArrival.value = false
+      DepartureList.value.Arrival = ''
+    }
+  } else {
+    isDeparture.value = false
+    DepartureList.value.Departure = ''
+    if (val.includes('Arrival Delayed (ATA-ETA)')) {
+      isArrival.value = true
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    } else {
+      isArrival.value = false
+      DepartureList.value.Arrival = ''
+    }
+  }
+  emit('ChangeCheckRules', DepartureList.value)
+}
+const changedeparture = (val: any) => {
+  if (val == 'Departure') {
+    Departurestr = 'Departure Delayed' + ' ≥ ' + clampedValue.value + ' ' + DepartureSelect.value
+    if (DepartureSelect.value != '') {
+      DepartureList.value.Departure = Departurestr
+    }
+    if (val == 'Arrival') {
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    }
+  } else {
+    if (val == 'Arrival') {
+      Arrivalstr =
+        'Arrival Delayed (ATA-ETA)' + ' ≥ ' + clampedArrivalValue.value + ' ' + ArrivalSelect.value
+      if (ArrivalSelect.value != '') {
+        DepartureList.value.Arrival = Arrivalstr
+      }
+    }
+  }
+  emit('ChangeCheckRules', DepartureList.value)
+}
+const closeDelayed = (val: any) => {
+  if (val.includes('Departure')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('Departure Delayed'), 1)
+    DepartureList.value.Departure = ''
+    isDeparture.value = false
+    DepartureTime.value = ''
+    DepartureSelect.value = ''
+  }
+  if (val.includes('Arrival')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('Arrival Delayed (ATA-ETA)'), 1)
+    DepartureList.value.Arrival = ''
+    isArrival.value = false
+    ArrivalTime.value = ''
+    ArrivalSelect.value = ''
+  }
+  emit('ChangeCheckRules', DepartureList.value)
+}
+
+// 清除所有数据
+const ClearData = () => {
+  OceanCheckedList.value = []
+  DepartureList.value.Departure = ''
+  isDeparture.value = false
+  DepartureList.value.Arrival = ''
+  isArrival.value = false
+  DepartureTime.value = ''
+  DepartureSelect.value = ''
+  ArrivalTime.value = ''
+  ArrivalSelect.value = ''
+}
+
+defineExpose({
+  closeDelayed,
+  ClearData
+})
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="DelayedShipments">
+        <template #title>
+          <div class="Rules_Title OceanTitle">{{ props.Title }}</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
+            <el-checkbox class="delayedType" value="Departure Delayed">
+              <div>Departure Delayed (ATD-ETD)</div>
+              <div v-if="isDeparture" style="margin-top: 16px">
+                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedIcon">></span>
+                <el-input
+                  v-model="DepartureTime"
+                  class="input-with-select"
+                  :value="clampedValue"
+                  @input="changedeparture('Departure')"
+                >
+                  <template #append>
+                    <el-select
+                      v-model="DepartureSelect"
+                      placeholder="Select"
+                      @change="changedeparture('Departure')"
+                    >
+                      <el-option label="Day(s)" value="Day(s)" />
+                      <el-option label="Hour(s)" value="Hour(s)" />
+                    </el-select>
+                  </template>
+                </el-input>
+              </div>
+            </el-checkbox>
+            <el-checkbox class="delayedType" value="Arrival Delayed (ATA-ETA)">
+              <div>Arrival Delayed (ATA-ETA)</div>
+              <div v-if="isArrival" style="margin-top: 16px">
+                <span class="delayedTitle">Delayed Time</span>
+                <span class="delayedIcon">></span>
+                <el-input
+                  v-model="ArrivalTime"
+                  @input="changedeparture('Arrival')"
+                  :value="clampedArrivalValue"
+                  class="input-with-select"
+                >
+                  <template #append>
+                    <el-select
+                      v-model="ArrivalSelect"
+                      placeholder="Select"
+                      @change="changedeparture('Arrival')"
+                    >
+                      <el-option label="Day(s)" value="Day(s)" />
+                      <el-option label="Hour(s)" value="Hour(s)" />
+                    </el-select>
+                  </template>
+                </el-input>
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+.delayedType {
+  align-items: start;
+  height: fit-content;
+  margin-right: 5px;
+  border-radius: 6px;
+  padding: 8px;
+}
+.input-with-select {
+  width: 180px;
+}
+:deep(.el-input__wrapper) {
+  width: 50%;
+}
+:deep(.el-input-group__append) {
+  width: 50%;
+  padding: 0;
+  border: none;
+}
+:deep(.el-input-group--append .el-input-group__append .el-select .el-select__wrapper) {
+  padding: 5px 10px;
+  background-color: #fff;
+}
+.delayedTitle {
+  color: var(--color-neutral-2);
+  font-size: 12px;
+}
+.delayedIcon {
+  margin: 0 8px;
+}
+.oceanCheckbox {
+  margin-bottom: 8px;
+}
+</style>

+ 341 - 0
src/components/CreateAddRules/src/components/ETDShipments.vue

@@ -0,0 +1,341 @@
+<script lang="ts" setup>
+import { ref, computed, watch } from 'vue'
+
+const props = defineProps({
+  Title: String,
+  ETDData: Object
+})
+const ETD_data = ref(props.ETDData)
+watch(
+  () => props.ETDData,
+  (current) => {
+    ETD_data.value = current
+    ETDInit()
+  }
+)
+
+const ETDInit = () => {
+  OceanCheckedList.value = []
+  if (ETD_data.value?.ETDradio == 't') {
+    ETDRadio.value = '1'
+    OceanCheckedList.value.push('ETD')
+  } else {
+    if (ETD_data.value?.etd_old_sub_new != '' && ETD_data.value?.etd_old_sub_new != undefined) {
+      ETDRadio.value = '2'
+      isETD.value = true
+      ETDTime.value = ETD_data.value?.etd_old_sub_new
+      ETDSelect.value = ETD_data.value?.etd_old_sub_new_unit
+      OceanCheckedList.value.push('ETD')
+    }
+  }
+  if (ETD_data.value?.ETAradio == 't') {
+    ETARadio.value = '1'
+    OceanCheckedList.value.push('ETA')
+  } else {
+    if (ETD_data.value?.eta_old_sub_new != '' && ETD_data.value?.eta_old_sub_new != undefined) {
+      ETARadio.value = '2'
+      isETA.value = true
+      ETATime.value = ETD_data.value?.eta_old_sub_new
+      ETASelect.value = ETD_data.value?.eta_old_sub_new_unit
+      OceanCheckedList.value.push('ETA')
+    }
+  }
+  CheckChange(OceanCheckedList.value)
+  changeETDRadio(ETDRadio.value)
+  changeETARadio(ETARadio.value)
+  changedeparture(OceanCheckedList.value)
+}
+const isETD = ref(false)
+const isETA = ref(false)
+const OceanActive = ref(['ETDShipments'])
+const OceanCheckedList = ref()
+const ETDTime = ref('')
+const ETDSelect = ref('')
+const ETATime = ref('')
+const ETASelect = ref('')
+const ETDRadio = ref()
+const ETARadio = ref()
+const emit = defineEmits(['ChangeCheckRules', 'closeETD'])
+const ETDETAList = ref({
+  ETD: '',
+  ETA: ''
+})
+let ETDstr: any = ''
+let ETAstr: any = ''
+const CheckChange = (val: any) => {
+  if (val.includes('ETD')) {
+    isETD.value = true
+    if (ETDSelect.value != '') {
+      ETDETAList.value.ETD = ETDstr
+    }
+    if (val.includes('ETA')) {
+      isETA.value = true
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    } else {
+      isETA.value = false
+      ETDETAList.value.ETA = ''
+    }
+  } else {
+    isETD.value = false
+    ETDETAList.value.ETD = ''
+    if (val.includes('ETA')) {
+      isETA.value = true
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    } else {
+      isETA.value = false
+      ETDETAList.value.ETA = ''
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const changeETDRadio = (val: any) => {
+  if (val == 1) {
+    ETDstr = 'ETD: Notify for all changes'
+    ETDETAList.value.ETD = ETDstr
+  } else if (val == 2) {
+    ETDstr = 'ETD: Notify for all changes ≥ ' + clampedValue.value + ' ' + ETDSelect.value
+    if (ETDSelect.value != '') {
+      ETDETAList.value.ETD = ETDstr
+    } else {
+      ETDETAList.value.ETD = ''
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const changeETARadio = (val: any) => {
+  if (val == 1) {
+    ETAstr = 'ETA: Notify for all changes'
+    ETDETAList.value.ETA = ETAstr
+  } else if (val == 2) {
+    ETAstr = 'ETA: Notify for all changes ≥ ' + clampedETAValue.value + ' ' + ETASelect.value
+    if (ETASelect.value != '') {
+      ETDETAList.value.ETA = ETAstr
+    } else {
+      ETDETAList.value.ETA = ''
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const changedeparture = (val: any) => {
+  if (val == 'ETD') {
+    ETDstr = 'ETD: Notify for all changes ≥ ' + clampedValue.value + ' ' + ETDSelect.value
+    if (ETDSelect.value != '') {
+      ETDETAList.value.ETD = ETDstr
+    }
+    if (val == 'ETA') {
+      ETAstr = 'ETA: Notify for all changes ≥ ' + clampedETAValue.value + ' ' + ETASelect.value
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    }
+  } else {
+    if (val == 'ETA') {
+      ETAstr = 'ETA: Notify for all changes ≥ ' + clampedETAValue.value + ' ' + ETASelect.value
+      if (ETASelect.value != '') {
+        ETDETAList.value.ETA = ETAstr
+      }
+    }
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+const closeETD = (val: any) => {
+  if (val.includes('ETD')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('ETD'), 1)
+    ETDETAList.value.ETD = ''
+    isETD.value = false
+    ETDTime.value = ''
+    ETDSelect.value = ''
+    ETDRadio.value = 0
+  }
+  if (val.includes('ETA')) {
+    OceanCheckedList.value.splice(OceanCheckedList.value.indexOf('ETA'), 1)
+    ETDETAList.value.ETA = ''
+    isETA.value = false
+    ETATime.value = ''
+    ETASelect.value = ''
+    ETARadio.value = 0
+  }
+  emit('ChangeCheckRules', ETDETAList.value)
+}
+
+// 清除所有数据
+const ClearData = () => {
+  OceanCheckedList.value = []
+  ETDETAList.value.ETD = ''
+  isETD.value = false
+  ETDETAList.value.ETA = ''
+  isETA.value = false
+  ETDRadio.value = 0
+  ETARadio.value = 0
+  ETDTime.value = ''
+  ETDSelect.value = ''
+  ETATime.value = ''
+  ETASelect.value = ''
+}
+
+defineExpose({
+  closeETD,
+  ClearData
+})
+
+const clampedValue = computed(() => {
+  if (ETDTime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETDTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+const clampedETAValue = computed(() => {
+  if (ETATime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETATime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="ETDShipments">
+        <template #title>
+          <div class="Rules_Title OceanTitle">{{ props.Title }}</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="OceanCheckedList">
+            <el-checkbox class="delayedType" value="ETD">
+              <div>ETD</div>
+              <div v-if="isETD" style="margin-top: 16px">
+                <el-radio-group v-model="ETDRadio" @change="changeETDRadio">
+                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="2"
+                    >Notify only when time difference
+                    <span class="delayedIcon">></span>
+                    <el-input
+                      v-model="ETDTime"
+                      :value="clampedValue"
+                      class="input-with-select"
+                      @input="changedeparture('ETD')"
+                    >
+                      <template #append>
+                        <el-select
+                          v-model="ETDSelect"
+                          placeholder="Select"
+                          @change="changedeparture('ETD')"
+                        >
+                          <el-option label="Day(s)" value="Day(s)" />
+                          <el-option label="Hour(s)" value="Hour(s)" />
+                        </el-select>
+                      </template> </el-input
+                  ></el-radio>
+                </el-radio-group>
+              </div>
+            </el-checkbox>
+            <el-checkbox class="delayedType" value="ETA">
+              <div>ETA</div>
+              <div v-if="isETA" style="margin-top: 16px">
+                <el-radio-group v-model="ETARadio" @change="changeETARadio">
+                  <el-radio value="1">Notify for all changes</el-radio>
+                  <el-radio value="2"
+                    >Notify only when time difference
+                    <span class="delayedIcon">></span>
+                    <el-input
+                      v-model="ETATime"
+                      :value="clampedETAValue"
+                      class="input-with-select"
+                      @input="changedeparture('ETA')"
+                    >
+                      <template #append>
+                        <el-select
+                          v-model="ETASelect"
+                          placeholder="Select"
+                          @change="changedeparture('ETA')"
+                        >
+                          <el-option label="Day(s)" value="Day(s)" />
+                          <el-option label="Hour(s)" value="Hour(s)" />
+                        </el-select>
+                      </template> </el-input
+                  ></el-radio>
+                </el-radio-group>
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+:deep(.el-radio) {
+  border: none !important;
+}
+.delayedType {
+  align-items: start;
+  height: fit-content;
+  margin-right: 5px;
+  border-radius: 6px;
+  padding: 8px;
+}
+.input-with-select {
+  width: 180px;
+}
+:deep(.el-input__wrapper) {
+  width: 50%;
+}
+:deep(.el-input-group__append) {
+  width: 50%;
+  padding: 0;
+  border: none;
+}
+:deep(.el-input-group--append .el-input-group__append .el-select .el-select__wrapper) {
+  padding: 5px 10px;
+  background-color: #fff;
+}
+.delayedIcon {
+  margin: 0 8px;
+}
+.oceanCheckbox {
+  margin-bottom: 8px;
+}
+:deep(.el-radio-group) {
+  display: block;
+}
+:deep(.el-radio) {
+  display: flex;
+  min-height: 32px;
+  border: 1px solid var(--color-select-border);
+  margin-bottom: 4px;
+  border-radius: 6px;
+  padding: 0 8px;
+  margin-right: 0;
+  height: fit-content;
+  line-height: 32px;
+  align-items: center;
+}
+:deep(.el-radio__input.is-checked + .el-radio__label) {
+  color: var(--color-neutral-1);
+}
+</style>

+ 398 - 0
src/components/CreateAddRules/src/components/NotiFrequency.vue

@@ -0,0 +1,398 @@
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import moment from 'moment-timezone'
+import { defaultTimeZone } from '@/utils/timezone'
+
+const props = defineProps({
+  FrequencyData: Object
+})
+
+const frequency_data = ref(props.FrequencyData)
+const radio = ref(0)
+const FrequencyList = ref()
+const DailyTime = ref('')
+const WeeklyTime = ref('')
+const WeeklyDay = ref('')
+const WeekDay = ref([
+  {
+    label: 'Monday',
+    value: 'Monday'
+  },
+  {
+    label: 'Tuesday',
+    value: 'Tuesday'
+  },
+  {
+    label: 'Wednesday',
+    value: 'Wednesday'
+  },
+  {
+    label: 'Thursday',
+    value: 'Thursday'
+  },
+  {
+    label: 'Friday',
+    value: 'Friday'
+  },
+  {
+    label: 'Saturday',
+    value: 'Saturday'
+  },
+  {
+    label: 'Sunday',
+    value: 'Sunday'
+  }
+])
+const TimeZone = ref([
+  {
+    label: 'UTC-08',
+    value: 'UTC-08'
+  },
+  {
+    label: 'UTC-07',
+    value: 'UTC-07'
+  },
+  {
+    label: 'UTC-06',
+    value: 'UTC-06'
+  },
+  {
+    label: 'UTC-05',
+    value: 'UTC-05'
+  },
+  {
+    label: 'UTC-04',
+    value: 'UTC-04'
+  },
+  {
+    label: 'UTC-03',
+    value: 'UTC-03'
+  },
+  {
+    label: 'UTC-02',
+    value: 'UTC-02'
+  },
+  {
+    label: 'UTC-01',
+    value: 'UTC-01'
+  },
+  {
+    label: 'UTC-00',
+    value: 'UTC-00'
+  },
+  {
+    label: 'UTC+01',
+    value: 'UTC+01'
+  },
+  {
+    label: 'UTC+02',
+    value: 'UTC+02'
+  },
+  {
+    label: 'UTC+03',
+    value: 'UTC+03'
+  },
+  {
+    label: 'UTC+04',
+    value: 'UTC+04'
+  },
+  {
+    label: 'UTC+05',
+    value: 'UTC+05'
+  },
+  {
+    label: 'UTC+05:30',
+    value: 'UTC+05:30'
+  },
+  {
+    label: 'UTC+06',
+    value: 'UTC+06'
+  },
+  {
+    label: 'UTC+07',
+    value: 'UTC+07'
+  },
+  {
+    label: 'UTC+08',
+    value: 'UTC+08'
+  },
+  {
+    label: 'UTC+09',
+    value: 'UTC+09'
+  },
+  {
+    label: 'UTC+10',
+    value: 'UTC+10'
+  },
+  {
+    label: 'UTC+11',
+    value: 'UTC+11'
+  },
+  {
+    label: 'UTC+12',
+    value: 'UTC+12'
+  }
+])
+const TimeZoneDailySelect = ref()
+const TimeZoneWeeklySelect = ref()
+TimeZoneDailySelect.value = 'UTC' + moment().tz(moment.tz.guess()).format('Z').slice(0, 3)
+TimeZoneWeeklySelect.value = 'UTC' + moment().tz(moment.tz.guess()).format('Z').slice(0, 3)
+const isDaily = ref(false)
+const isWeekly = ref(false)
+let savesubscribeobj: any = {}
+
+watch(
+  () => props.FrequencyData,
+  (current) => {
+    frequency_data.value = current
+    FrequencyDataInit()
+  }
+)
+
+const emits = defineEmits(['ChangeFrequencyAdd'])
+
+// 初始设置Frequency
+const FrequencyDataInit = () => {
+  if (frequency_data.value?.frequency_type == 'Instant') {
+    radio.value = 1
+    ChangeFrequency(1)
+  } else if (frequency_data.value?.frequency_type == 'Daily') {
+    radio.value = 2
+    DailyTime.value = frequency_data.value?.daily_time
+    TimeZoneDailySelect.value = frequency_data.value?.daily_time_zone
+    ChangeFrequency(2)
+  } else if (frequency_data.value?.frequency_type == 'Weekly') {
+    radio.value = 3
+    WeeklyDay.value = frequency_data.value?.weekly_week
+    WeeklyTime.value = frequency_data.value?.weekly_time
+    TimeZoneWeeklySelect.value = frequency_data.value?.weekly_time_zone
+    ChangeFrequency(3)
+  } else {
+    radio.value = 0
+    ChangeFrequency(0)
+  }
+}
+
+// 更改Frequency时间
+const ChangeFrequency = (val: any) => {
+  FrequencyList.value = []
+  let str: any = ''
+  if (val == 1) {
+    isDaily.value = false
+    isWeekly.value = false
+    str = 'Instant notification for each update'
+    FrequencyList.value.push(str)
+    savesubscribeobj.frequency_type = 'Instant'
+    savesubscribeobj.frequency_display = str
+  } else if (val == 2) {
+    isDaily.value = true
+    isWeekly.value = false
+    str = 'Daily, ' + DailyTime.value + ', ' + TimeZoneDailySelect.value
+    if (DailyTime.value != '' && TimeZoneDailySelect.value !== '') {
+      FrequencyList.value.push(str)
+    }
+    savesubscribeobj.frequency_type = 'Daily'
+    savesubscribeobj.daily_time = DailyTime.value
+    savesubscribeobj.daily_time_zone = TimeZoneDailySelect.value
+    savesubscribeobj.frequency_display = str
+  } else if (val == 3) {
+    isDaily.value = false
+    isWeekly.value = true
+    str = 'Weekly, ' + WeeklyDay.value + ', ' + WeeklyTime.value + ', ' + TimeZoneWeeklySelect.value
+    if (WeeklyDay.value != '' && WeeklyTime.value != '' && TimeZoneWeeklySelect.value !== '') {
+      FrequencyList.value.push(str)
+    }
+    savesubscribeobj.frequency_type = 'Weekly'
+    if (WeeklyDay.value == 'Monday') {
+      savesubscribeobj.weekly_week = 1
+    } else if (WeeklyDay.value == 'Tuesday') {
+      savesubscribeobj.weekly_week = 2
+    } else if (WeeklyDay.value == 'Wednesday') {
+      savesubscribeobj.weekly_week = 3
+    } else if (WeeklyDay.value == 'Thursday') {
+      savesubscribeobj.weekly_week = 4
+    } else if (WeeklyDay.value == 'Friday') {
+      savesubscribeobj.weekly_week = 5
+    } else if (WeeklyDay.value == 'Saturday') {
+      savesubscribeobj.weekly_week = 6
+    } else if (WeeklyDay.value == 'Sunday') {
+      savesubscribeobj.weekly_week = 0
+    }
+    savesubscribeobj.weekly_time = WeeklyTime.value
+    savesubscribeobj.weekly_time_zone = TimeZoneWeeklySelect.value
+    savesubscribeobj.frequency_display = str
+  } else {
+    isDaily.value = false
+    isWeekly.value = false
+    DailyTime.value = ''
+    WeeklyTime.value = ''
+    WeeklyDay.value = ''
+    delete savesubscribeobj.frequency_type
+    delete savesubscribeobj.daily_time
+    delete savesubscribeobj.daily_time_zone
+    delete savesubscribeobj.weekly_week
+    delete savesubscribeobj.weekly_time
+    delete savesubscribeobj.weekly_time_zone
+  }
+  emits('ChangeFrequencyAdd', FrequencyList.value, savesubscribeobj)
+}
+const changeTime = (val: any) => {
+  if (val == 'Daily') {
+    ChangeFrequency(2)
+  } else {
+    ChangeFrequency(3)
+  }
+}
+
+// 删除 Frequency tag
+const handleCloseRadioFrequency = () => {
+  radio.value = 0
+  ChangeFrequency(0)
+}
+
+const user_type = localStorage.getItem('user_type')
+
+defineExpose({
+  handleCloseRadioFrequency,
+  FrequencyDataInit
+})
+</script>
+<template>
+  <div style="margin-top: 11px">
+    <el-radio-group v-model="radio" @change="ChangeFrequency">
+      <el-radio :value="1" v-if="user_type != null && user_type != 'customer'"
+        >Instant notification for each update</el-radio
+      >
+      <el-radio :value="2">
+        <div>Daily Summary</div>
+        <div class="Daily" v-if="isDaily">
+          <div class="Daily_left" style="margin-right: 8px">
+            Select Time
+            <div>
+              <el-time-select
+                v-model="DailyTime"
+                start="00:00"
+                step="00:30"
+                end="23:30"
+                prefix-icon=""
+                @change="changeTime('Daily')"
+                placeholder="Select Time"
+              ></el-time-select>
+            </div>
+          </div>
+          <div class="Daily_left">
+            Select Time Zone
+            <div>
+              <el-select
+                v-model="TimeZoneDailySelect"
+                placeholder="Select Time Zone"
+                @change="changeTime('Daily')"
+              >
+                <el-option
+                  v-for="item in TimeZone"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </div>
+          </div>
+        </div>
+      </el-radio>
+      <el-radio :value="3">
+        <div>Weekly Summary</div>
+        <div class="Daily" v-if="isWeekly">
+          <div class="Weekly_left">
+            Select Day
+            <div>
+              <el-select
+                v-model="WeeklyDay"
+                @change="changeTime('Weekly')"
+                placeholder="Select Day"
+              >
+                <el-option
+                  v-for="item in WeekDay"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </div>
+          </div>
+          <div class="Weekly_left" style="margin: 0 8px">
+            Select Time
+            <div>
+              <el-time-select
+                v-model="WeeklyTime"
+                @change="changeTime('Weekly')"
+                start="00:00"
+                step="00:30"
+                end="23:30"
+                prefix-icon=""
+                placeholder="Select time"
+              ></el-time-select>
+            </div>
+          </div>
+          <div class="Weekly_left">
+            Select Time Zone
+            <div>
+              <el-select
+                v-model="TimeZoneWeeklySelect"
+                placeholder="Select Time Zone"
+                @change="changeTime('Weekly')"
+              >
+                <el-option
+                  v-for="item in TimeZone"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </div>
+          </div>
+        </div>
+      </el-radio>
+    </el-radio-group>
+  </div>
+</template>
+<style lang="scss" scoped>
+:deep(.el-radio-group) {
+  display: block;
+}
+:deep(.el-radio) {
+  display: flex;
+  min-height: 32px;
+  border: 1px solid var(--color-select-border);
+  margin-bottom: 4px;
+  border-radius: 6px;
+  padding: 0 8px;
+  margin-right: 0;
+  height: fit-content;
+  line-height: 32px;
+  align-items: start;
+}
+:deep(.el-radio__input.is-checked + .el-radio__label) {
+  color: var(--color-neutral-1);
+}
+.Daily {
+  margin: 0 0 9px 0;
+  display: flex;
+}
+.Daily_left {
+  color: var(--color-neutral-2);
+  width: 50%;
+}
+.Weekly_left {
+  color: var(--color-neutral-2);
+  width: 33%;
+}
+:deep(.el-select__icon.el-icon svg) {
+  width: 1em !important;
+}
+:deep(.el-select__wrapper.is-filterable) {
+  padding-left: 7px;
+}
+:deep(.el-radio__inner) {
+  margin-top: 7px;
+}
+</style>

+ 92 - 0
src/components/CreateAddRules/src/components/NotiMethods.vue

@@ -0,0 +1,92 @@
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+
+const checkMethodList = ref()
+checkMethodList.value = []
+let savesubscribeobj: any = {}
+const props = defineProps({
+  MethodsData: Object
+})
+
+const methods_data = ref(props.MethodsData)
+watch(
+  () => props.MethodsData,
+  (current) => {
+    methods_data.value = current
+    MethodsInit()
+  }
+)
+const emits = defineEmits(['ChangeMethodsAdd'])
+// 初始设置Methods
+const MethodsInit = () => {
+  if (methods_data.value?.method_display != undefined) {
+    if (methods_data.value?.method_display.indexOf('Email') != -1) {
+      checkMethodList.value.push('By Email')
+    }
+  }
+  if (methods_data.value?.method_display != undefined) {
+    if (methods_data.value?.method_display.indexOf('System Message') != -1) {
+      checkMethodList.value.push('By System Message')
+    }
+  }
+  changeMethod(checkMethodList.value)
+}
+
+// 选中Method
+const changeMethod = (val: any) => {
+  if (val.indexOf('By Email') != -1) {
+    savesubscribeobj.method_by_email = true
+  } else {
+    savesubscribeobj.method_by_email = false
+  }
+  if (val.indexOf('By System Message ') != -1) {
+    savesubscribeobj.method_by_message = true
+  } else {
+    savesubscribeobj.method_by_message = false
+  }
+  if (val.length != 0) {
+    savesubscribeobj.method_display = val.map((e: any) => e.replace('By ', '')).join(',')
+  } else {
+    savesubscribeobj.method_display = ''
+  }
+  emits('ChangeMethodsAdd', checkMethodList.value, savesubscribeobj)
+}
+const user_type = localStorage.getItem('user_type')
+</script>
+<template>
+  <div style="margin-top: 11px">
+    <div class="Method">
+      <el-checkbox-group v-model="checkMethodList" @change="changeMethod">
+        <el-checkbox
+          class="methodcheckbox"
+          value="By Email"
+          v-if="user_type != null && user_type != 'customer'"
+        >
+          <div>By Email</div>
+          <div class="methos_image"><img src="../images/illustration_email@2x.png" /></div>
+        </el-checkbox>
+        <el-checkbox class="methodcheckbox" value="By System Message">
+          <div>By System Message</div>
+          <div class="methos_image">
+            <img src="../images/illustration_system massage@2x.png" />
+          </div>
+        </el-checkbox>
+      </el-checkbox-group>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.methodcheckbox {
+  align-items: start;
+  height: fit-content;
+  width: 49%;
+  margin-right: 5px;
+  background-color: var(--color-dialog-header-bg);
+  border-radius: 6px;
+  padding: 11px 0 0 13px;
+}
+.methos_image {
+  margin: 9px 0;
+}
+</style>

+ 83 - 0
src/components/CreateAddRules/src/components/RulesShipments.vue

@@ -0,0 +1,83 @@
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+
+interface OceanCheckboxItem {
+  value: string
+  label: string
+}
+interface Props {
+  CheckboxList: OceanCheckboxItem[]
+  Title: String
+  CheckedList: Array<''>
+}
+const props = defineProps<Props>()
+
+const OceanActive = ref(['OceanShipments'])
+const CheckedList = ref(props.CheckedList)
+const CheckboxList = ref(props.CheckboxList)
+watch(
+  () => props.CheckboxList,
+  (current) => {
+    CheckboxList.value = current
+  }
+)
+watch(
+  () => props.CheckedList,
+  (current) => {
+    CheckedList.value = current
+  }
+)
+
+const emit = defineEmits(['ChangeCheckRules'])
+const CheckChange = () => {
+  console.log(CheckedList.value)
+  emit('ChangeCheckRules', CheckedList.value)
+}
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="OceanShipments">
+        <template #title>
+          <div class="Rules_Title OceanTitle">{{ props.Title }}</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="CheckedList">
+            <el-checkbox
+              v-for="item in CheckboxList"
+              :key="item.label"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+.oceanCheckbox {
+  max-height: 321px;
+  overflow-x: hidden;
+  margin-bottom: 8px;
+  overflow-y: scroll;
+}
+</style>

+ 197 - 0
src/components/CreateAddRules/src/components/ShipmentRange.vue

@@ -0,0 +1,197 @@
+<script lang="ts" setup>
+import { ref, computed } from 'vue'
+
+const OceanActive = ref(['TransportMode', 'Time'])
+const TransportCheckedList = ref([])
+interface OceanItem {
+  label: string
+  value: string
+}
+const TransportList = ref<OceanItem[]>([])
+TransportList.value = [
+  {
+    label: 'Ocean',
+    value: 'Ocean'
+  },
+  {
+    label: 'Air',
+    value: 'Air'
+  },
+  {
+    label: 'Road',
+    value: 'Road'
+  }
+]
+
+const TimeChecked = ref()
+
+const ETDTime = ref('')
+const ETATime = ref('')
+
+const clampedETDValue = computed(() => {
+  if (ETDTime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETDTime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+const clampedETAValue = computed(() => {
+  if (ETATime.value == '') {
+    return 0
+  } else {
+    const numericValue = ETATime.value.replace(/[^0-9]/g, 0) // 移除非数字字符
+    return Math.min(Math.max(parseInt(numericValue, 10), 0), 365) // 确保值在0到1000之间,但不更新原输入值仅用于显示
+  }
+})
+
+let Transportstr: any = ''
+let Timestr: any = ''
+const emit = defineEmits(['ChangeCheckRules', 'ChangeCheckTimeRules'])
+const CheckChange = (val: any) => {
+  if (val != '') {
+    Transportstr = 'Transport Mode: ' + val
+  } else {
+    Transportstr = ''
+  }
+  emit('ChangeCheckRules', Transportstr, TransportCheckedList.value)
+}
+
+// 输入ETD、ETA
+const changeTime = (val: any) => {
+  if (val == 1) {
+    Timestr = 'ETD within ' + clampedETDValue.value + ' Day(s)'
+  } else if (val == 2) {
+    Timestr = 'ETA within ' + clampedETAValue.value + ' Day(s)'
+  } else {
+    Timestr = ''
+  }
+  emit('ChangeCheckTimeRules', Timestr, clampedETDValue.value)
+}
+
+const handleCloseCreateRule = (val: any) => {
+  if (val.indexOf('ETD') != -1 || val.indexOf('ETA') != -1) {
+    TimeChecked.value = 0
+    ETDTime.value = ''
+    ETATime.value = ''
+  } else if (val.indexOf('Transport') != -1) {
+    TransportCheckedList.value = []
+  }
+}
+
+defineExpose({
+  handleCloseCreateRule
+})
+</script>
+<template>
+  <div class="Ocean_collapse">
+    <el-collapse v-model="OceanActive">
+      <el-collapse-item name="TransportMode">
+        <template #title>
+          <div class="Rules_Title OceanTitle">Transport Mode</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="TransportCheckedList">
+            <el-checkbox
+              v-for="item in TransportList"
+              :key="item.label"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-checkbox-group>
+        </div>
+      </el-collapse-item>
+      <el-collapse-item name="Time">
+        <template #title>
+          <div class="Rules_Title OceanTitle">Time</div>
+        </template>
+        <div class="oceanCheckbox">
+          <el-radio-group v-model="TimeChecked" @change="changeTime">
+            <el-radio value="1">
+              <div class="flex">
+                <div style="width: 70px">ETD within</div>
+                <el-input
+                  @input="changeTime('1')"
+                  v-model="ETDTime"
+                  :value="clampedETDValue"
+                  class="input-with-select"
+                >
+                </el-input>
+                <div class="Days">Day(s)</div>
+              </div>
+            </el-radio>
+            <el-radio value="2">
+              <div class="flex">
+                <div style="width: 70px">ETA within</div>
+                <el-input
+                  @input="changeTime('2')"
+                  v-model="ETATime"
+                  :value="clampedETAValue"
+                  class="input-with-select"
+                >
+                </el-input>
+                <div class="Days">Day(s)</div>
+              </div>
+            </el-radio>
+          </el-radio-group>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-select-border);
+  border-radius: 5px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-drawer-body-bg);
+  border-bottom: 1px solid var(--color-select-border);
+  padding: 8px;
+}
+:deep(.el-checkbox:first-child) {
+  border-radius: 6px 6px 0 0;
+}
+:deep(.el-checkbox:last-child) {
+  border-radius: 0 0 6px 6px;
+  border-bottom: none;
+}
+.oceanCheckbox {
+  max-height: 321px;
+  overflow-x: hidden;
+  margin-bottom: 8px;
+  overflow-y: scroll;
+}
+
+.input-with-select {
+  width: 96px;
+  border-radius: 6px 0 0 6px;
+  margin-left: 8px;
+}
+:deep(.el-input__wrapper) {
+  border-radius: 6px 0 0 6px;
+  opacity: 0.8;
+  height: 28px;
+}
+.flex {
+  display: flex;
+  align-items: center;
+}
+.Days {
+  width: 64px;
+  border: 1px solid var(--color-select-border);
+  border-radius: 0 6px 6px 0;
+  background-color: var(--color-drawer-body-bg);
+  border-left: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  opacity: 0.8;
+  height: 28px;
+}
+:deep(.el-radio) {
+  background-color: var(--color-drawer-body-bg);
+}
+</style>

BIN
src/components/CreateAddRules/src/images/icon_collapse.png


BIN
src/components/CreateAddRules/src/images/icon_expand.png


BIN
src/components/CreateAddRules/src/images/illustration_email@2x.png


BIN
src/components/CreateAddRules/src/images/illustration_system massage@2x.png


BIN
src/components/CreateAddRules/src/images/submit_successful.png


+ 46 - 0
src/components/TableEmpty/TableEmpty.vue

@@ -0,0 +1,46 @@
+<script setup lang="ts">
+import { computed } from 'vue'
+import lightPng from './image/default_notification_setting.png'
+import darkPng from './image/default_notification_setting.png'
+import { useThemeStore } from '@/stores/modules/theme'
+
+const themeStore = useThemeStore()
+// 判断当前系统主题模式
+const emptyImg = computed(() => {
+  return themeStore.theme === 'dark' ? darkPng : lightPng
+})
+
+const props = defineProps({
+  EmptyTitle: String
+})
+</script>
+
+<template>
+  <div class="v-empty">
+    <div class="empty-img">
+      <img :src="emptyImg" alt="" />
+    </div>
+    <p class="title">
+      <slot name="title">{{ props.EmptyTitle }}</slot>
+    </p>
+    <div>
+      <slot name="suggestion"></slot>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.v-empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  .empty-img {
+    margin-bottom: 16px;
+  }
+  .title {
+    margin-bottom: 8px;
+    font-weight: 400;
+    color: var(--color-neutral-2);
+  }
+}
+</style>

BIN
src/components/TableEmpty/image/default_notification_setting.png


+ 1 - 0
src/components/TableEmpty/index.ts

@@ -0,0 +1 @@
+export { default } from './src/TableEmpty.vue'

+ 13 - 0
src/router/index.ts

@@ -87,6 +87,19 @@ const router = createRouter({
           path: '/Operationlog',
           name: 'Operationlog',
           component: () => import('../views/OperationLog')
+        },
+        {
+          path: '/SystemSettings',
+          name: 'Monitoring Settings',
+          component: () => import('../views/SystemSettings')
+        },
+        {
+          path: '/SystemSettings/createnewrule',
+          name: 'Create New Rule',
+          component: () => import('../views/SystemSettings/src/components/CreateNewrule'),
+          meta: {
+            activeMenu: '/SystemSettings'
+          }
         }
       ]
     }

+ 1 - 1
src/stores/modules/breadCrumb.ts

@@ -9,7 +9,7 @@ interface BreadCrumb {
   routeList: Route[]
 }
 // 需要添加多级菜单的页面,值为route的name
-const whiteList = ['Booking Detail', 'Tracking Detail', 'Add VGM', 'Public Tracking Detail']
+const whiteList = ['Booking Detail', 'Tracking Detail', 'Add VGM', 'Public Tracking Detail','Create New Rule']
 
 export const useBreadCrumb = defineStore('breadCrumb', {
   state: (): BreadCrumb => ({

+ 16 - 0
src/styles/elementui.scss

@@ -75,6 +75,19 @@ button.el-button--main {
     fill: var(--color-white);
   }
 }
+button.el-button--main.is-disabled {
+  border: none;
+  background-color: var(--color-btn-main-bg-disabled);
+  span {
+    color: var(--color-white);
+  }
+  fill: var(--color-white);
+  &:hover {
+    background-color: var(--color-btn-main-bg-disabled);
+    color: var(--color-white);
+    fill: var(--color-white);
+  }
+}
 
 button.el-button.el-button--pain-theme {
   border: 1px solid var(--color-el-btn-pain-theme-border);
@@ -782,3 +795,6 @@ div .DaterangeClass {
   border-color: var(--management-bg-color) !important;
   border-radius: 12px !important;
 }
+div .el-radio__label {
+  width: 100%;
+}

+ 32 - 4
src/styles/icons/iconfont.css

@@ -1,9 +1,9 @@
 @font-face {
   font-family: "font_family"; /* Project id 4672385 */
-  src: url('iconfont.woff2?t=1736324058852') format('woff2'),
-       url('iconfont.woff?t=1736324058852') format('woff'),
-       url('iconfont.ttf?t=1736324058852') format('truetype'),
-       url('iconfont.svg?t=1736324058852#font_family') format('svg');
+  src: url('iconfont.woff2?t=1737535437731') format('woff2'),
+       url('iconfont.woff?t=1737535437731') format('woff'),
+       url('iconfont.ttf?t=1737535437731') format('truetype'),
+       url('iconfont.svg?t=1737535437731#font_family') format('svg');
 }
 
 .font_family {
@@ -14,6 +14,34 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-icon_up_b:before {
+  content: "\e704";
+}
+
+.icon-icon_route_b:before {
+  content: "\e700";
+}
+
+.icon-icon_model_b:before {
+  content: "\e701";
+}
+
+.icon-icon_delay_b:before {
+  content: "\e702";
+}
+
+.icon-icon_container__filled_b:before {
+  content: "\e703";
+}
+
+.icon-icon_po__sscc_b:before {
+  content: "\e6fe";
+}
+
+.icon-icon_po__request_b:before {
+  content: "\e6ff";
+}
+
 .icon-icon_currentlink_b:before {
   content: "\e6fc";
 }

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
src/styles/icons/iconfont.js


+ 14 - 0
src/styles/icons/iconfont.svg

@@ -14,6 +14,20 @@
     />
       <missing-glyph />
       
+      <glyph glyph-name="icon_up_b" unicode="&#59140;" d="M503.488 519.104a32 32 0 0 0 44.992 0l227.072-224.64a32 32 0 0 0-22.528-54.784H299.008A32 32 0 0 0 276.48 294.4l227.008 224.704z"  horiz-adv-x="1088" />
+      
+      <glyph glyph-name="icon_route_b" unicode="&#59136;" d="M428.544 674.816a131.008 131.008 0 1 0-0.768-76.8H332.672a89.024 89.024 0 0 1 0-178.112h387.2a165.312 165.312 0 0 0 0-330.56h-43.072a130.944 130.944 0 1 0 3.2 76.8h39.872a88.512 88.512 0 1 1 0 176.96h-387.2a165.824 165.824 0 1 0 0 331.712h95.872z"  horiz-adv-x="1088" />
+      
+      <glyph glyph-name="icon_model_b" unicode="&#59137;" d="M540.096 858.112A28.8 28.8 0 0 1 515.904 858.24L80 657.6a28.8 28.8 0 0 1-14.272-37.76v-483.264a28.8 28.8 0 0 1 16.64-26.112l428.928-200.32a28.8 28.8 0 0 1 24.768-0.32l435.968 200.576a28.8 28.8 0 0 1 16.704 26.176V631.424a28.8 28.8 0 0 1-18.88 27.072L540.096 858.24z m12.8-445.76l378.24 174.08v-431.424l-378.24-174.08v431.36z m-429.568 172.8l371.84-172.8v-431.104l-371.84 173.632V585.152z m767.872 46.336L524.16 462.72 161.792 631.808l366.08 168.448 363.328-168.768z"  horiz-adv-x="1088" />
+      
+      <glyph glyph-name="icon_delay_b" unicode="&#59138;" d="M489.984 631.182222a56.888889 56.888889 0 0 0 96.142222-0.227555l288.483556-458.126223a56.888889 56.888889 0 0 0-48.128-87.210666H247.068444A56.888889 56.888889 0 0 0 199.111111 172.942222l290.929778 458.183111z m48.014222-30.492444l-290.929778-458.183111h579.413334L537.998222 600.689778z m27.136-146.773334v-180.906666h-56.888889V453.973333h56.888889z m0-275.000888v47.104h-56.888889v-47.104h56.888889z"  horiz-adv-x="1080" />
+      
+      <glyph glyph-name="icon_container__filled_b" unicode="&#59139;" d="M422.336 113.728V654.272a12.8 12.8 0 0 0 15.104 12.608l526.08-95.36a12.8 12.8 0 0 0 10.496-12.544v-344.064a12.8 12.8 0 0 0-10.432-12.544l-526.08-101.248a12.8 12.8 0 0 0-15.168 12.608z m78.464 57.856l23.168 3.584V596.672l-23.168 3.712v-428.8z m102.976 15.488l23.168 3.2V580.928l-23.168 3.776v-397.632z m102.976 381.44v-365.504l23.232 3.2V565.12l-23.232 3.328z m102.976-349.76l23.232 3.456V549.12l-23.232 3.52v-333.824z m103.04 318.08v-301.952l23.168 3.328v295.04l-23.232 3.584zM396.352 652.352v-537.6a12.8 12.8 0 0 0-14.976-12.672l-134.4 22.976V375.36h73.216v23.232H246.976V644.096l134.656 20.864a12.8 12.8 0 0 0 14.784-12.608z m-172.672-523.392v246.4H141.888v23.232h81.92v241.92l-134.976-20.928a12.8 12.8 0 0 1-10.88-12.672v-442.24a12.8 12.8 0 0 1 10.688-12.608l135.104-23.04z"  horiz-adv-x="1088" />
+      
+      <glyph glyph-name="icon_po__sscc_b" unicode="&#59134;" d="M216.768 864a32 32 0 0 1-32-32v-896a32 32 0 0 1 32-32h284.8v64h-252.8v832H579.2v-160.064a128 128 0 0 1 128-128h160.064v-176.384h64v204.8 1.92a32 32 0 0 1-9.344 24.32L633.728 854.528A32 32 0 0 1 608.448 864h-391.68z m426.432-112.128l175.424-176H707.2a64 64 0 0 0-64 64v112z m-63.552-640a143.808 143.808 0 1 0 287.616-0.064 143.808 143.808 0 0 0-287.616 0z m143.808 207.744a207.808 207.808 0 1 1 0-415.616 207.808 207.808 0 0 1 0 415.616z m80-207.808a80 80 0 1 0-160 0 80 80 0 0 0 160 0z"  horiz-adv-x="1088" />
+      
+      <glyph glyph-name="icon_po__request_b" unicode="&#59135;" d="M184.768 832a32 32 0 0 0 32 32h391.68a32 32 0 0 0 25.344-9.344l288.128-288.128a32 32 0 0 0 9.28-24.32l0.064-1.792v-270.784h-64V511.936H707.2a128 128 0 0 0-128 128V800H248.768v-832h309.248v-64H216.768a32 32 0 0 0-32 32V832z m633.856-256.064L643.2 751.872v-112a64 64 0 0 1 64-64h111.424z m-23.104-672v135.808h135.744v64H795.52v135.744h-64v-135.744H595.776v-64h135.68V-96h64z"  horiz-adv-x="1088" />
+      
       <glyph glyph-name="icon_currentlink_b" unicode="&#59132;" d="M102.4 384a409.6 409.6 0 1 0 819.2 0A409.6 409.6 0 0 0 102.4 384zM512 870.4a486.4 486.4 0 1 1 0-972.8A486.4 486.4 0 0 1 512 870.4z m0-742.336a256 256 0 1 1 0 512 256 256 0 0 1 0-512z"  horiz-adv-x="1024" />
       
       <glyph glyph-name="icon_jumplink_b1" unicode="&#59133;" d="M636.416 633.28h-252.16v96h368a48 48 0 0 0 48-48v-368h-96v252.16l-370.56-370.624-67.904 67.84 370.56 370.624z m305.6-610.24h-832v96h832v-96z"  horiz-adv-x="1088" />

BIN
src/styles/icons/iconfont.ttf


BIN
src/styles/icons/iconfont.woff


BIN
src/styles/icons/iconfont.woff2


+ 4 - 0
src/styles/theme.scss

@@ -96,12 +96,14 @@
   --color-btn-main-plain-bg-hover: hsl(26, 100%, 95%);
   // main
   --color-btn-main-bg-hover: #d56200;
+  --color-btn-main-bg-disabled: rgba(237, 109, 0, 0.3);
   // success
   --color-btn-success-bg-hover: #009765;
   // warning
   --color-btn-warning-bg: #e9b227;
   --color-btn-warning-bg-hover: #d2a023;
   // danger
+  --color-btn-danger-bg: #C9353F;
   --color-btn-danger-bg-hover: #b53039;
 
   --color-tag-checked-all: #fff2e8;
@@ -246,6 +248,8 @@
   --color-vxe-table-visited-row-bg: #f2f2f2;
 
   --color-public-tracking-empty-bg: #fff;
+  --color-dot-unchecked: #eeeeee;
+  --color-dot-checked: #ed6d00;
 
   --color-upload-file-bg: #fef8f2;
   --color-upload-file-color: #b5b9bf;

+ 27 - 0
src/utils/timezone.ts

@@ -0,0 +1,27 @@
+export const defaultTimeZone = (zone: any) => {
+  if(zone == 'zh-CN' || zone == 'zh-TW' || zone == 'zh-HK'|| zone == 'en-AU'|| zone == 'ms-MY' || zone == 'id-ID' ) {
+    return 'UTC+08'
+  } else if(zone == 'ja-JP' || zone == 'ko-KR') {
+    return 'UTC+09'
+  } else if(zone == 'en-US') {
+    return 'UTC-08'
+  } else if(zone == 'en-GB' || zone == 'pt-PT') {
+    return 'UTC+00'
+  } else if(zone == 'en-NZ' ) {
+    return 'UTC+12'
+  } else if(zone == 'de-DE' || zone == 'fr-FR' || zone == 'es-ES'|| zone == 'it-IT'|| zone == 'nl-NL'|| zone == 'pl-PL'|| zone == 'sv-SE'|| zone == 'da-DK'|| zone == 'no-NO') {
+    return 'UTC+01'
+  } else if(zone == 'ru-RU' || zone == 'fi-FI' || zone == 'he-IL'|| zone == 'el-GR') {
+    return 'UTC+02'
+  } else if(zone == 'es-MX') {
+    return 'UTC-06'
+  } else if(zone == 'pt-BR') {
+    return 'UTC-03'
+  } else if(zone == 'ar-SA' || zone == 'tr-TR') {
+    return 'UTC+03'
+  } else if(zone == 'hi-IN') {
+    return 'UTC+05:30'
+  } else if(zone == 'th-TH' || zone == 'vi-VN') {
+    return 'UTC+07'
+  }
+}

+ 21 - 6
src/views/Dashboard/src/components/RecentStatus.vue

@@ -7,7 +7,7 @@ interface RecentItem {
   Title: string
   name: string
   bookingNumber: string
-  IsSubscribe: boolean
+  is_subscribe: boolean
   shipperName: string
   consigneeName: string
   startStation: string
@@ -34,6 +34,21 @@ const RouteToDetail = (val: any) => {
     }
   })
 }
+
+// 订阅收藏
+const SubscribeShipments = (val: any) => {
+  val.is_subscribe = !val.is_subscribe
+  $api
+    .SubscribeShipments({
+      serial_no: val.a,
+      is_subscribe: val.is_subscribe
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        console.log(res.data)
+      }
+    })
+}
 </script>
 <template>
   <div class="recent_route" v-for="(item, index) in props.RecentStatusList" :key="index">
@@ -68,13 +83,13 @@ const RouteToDetail = (val: any) => {
           <span>{{ item.bookingNumber }}</span>
         </div>
       </div>
-      <!-- <div class="recent-header-right">
+      <div class="recent-header-right">
         <el-button
           class="recent_button"
-          @click="item.IsSubscribe = !item.IsSubscribe"
-          :class="item.IsSubscribe ? 'IsSubscribe' : ''"
+          @click="SubscribeShipments(item)"
+          :class="item.is_subscribe ? 'IsSubscribe' : ''"
         >
-          <span v-if="item.IsSubscribe" class="iconfont_icon">
+          <span v-if="item.is_subscribe" class="iconfont_icon">
             <svg class="iconfont" aria-hidden="true">
               <use xlink:href="#icon-icon_marked_b"></use>
             </svg>
@@ -86,7 +101,7 @@ const RouteToDetail = (val: any) => {
           </span>
           <span class="Subscribe">Subscribe</span>
         </el-button>
-      </div> -->
+      </div>
     </div>
     <div class="recent_content">
       <!-- 左 -->

+ 1 - 0
src/views/Layout/src/components/Header/components/LogoutDialog.vue

@@ -12,6 +12,7 @@ const handleLogout = () => {
   dialogVisible.value = false
   router.push('/login')
   sessionStorage.clear()
+  localStorage.removeItem('user_type')
 }
 defineExpose({
   openDialog

+ 1 - 0
src/views/Login/src/loginView.vue

@@ -243,6 +243,7 @@ const handleLoginAfterVerify = () => {
       if (res.code === 200) {
         if (isRememerPwd.value) {
           saveCredentials()
+          localStorage.setItem('user_type', res.data.user_info.user_type)
         } else {
           clearCredentials()
         }

+ 2 - 2
src/views/OperationLog/src/components/BookingTable/src/BookingTable.vue

@@ -111,7 +111,7 @@ const getTableData = async (isPageChange?: boolean) => {
     .SearchOperationLog({
       cp: pageInfo.value.pageNo,
       ps: pageInfo.value.pageSize,
-      rc: -1,
+      rc,
       ...searchdata
     })
     .then((res: any) => {
@@ -153,7 +153,7 @@ const SearchOperationLog = (val: any) => {
     })
 }
 onMounted(() => {
-  Promise.all([getTableColumns(), getTableData()]).finally(() => {
+  Promise.all([getTableColumns(), getTableData(false)]).finally(() => {
     nextTick(() => {
       // tableRef.value && autoWidth(bookingTable.value, tableRef.value)
     })

+ 1 - 0
src/views/SystemSettings/index.ts

@@ -0,0 +1 @@
+export { default } from './src/SystemSettings.vue'

+ 458 - 0
src/views/SystemSettings/src/SystemSettings.vue

@@ -0,0 +1,458 @@
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import AddRSettingTableules from './components/SettingTable'
+import MonitoringTable from './components/MonitoringTable'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+
+const TabActive = ref('Subscribe Notifications')
+const isMilestoneChecked = ref(false)
+const isContainerChecked = ref(false)
+const isDepartureChecked = ref(false)
+const isETDChangeChecked = ref(false)
+const isMilestoneAdded = ref(false)
+const isContainerAdded = ref(false)
+const isDepartureAdded = ref(false)
+const isETDChangeAdded = ref(false)
+const CollapseActive = ref()
+const isActiveCollapse = ref({
+  Milestone: false,
+  Container: false,
+  Departure: false,
+  ETDChange: false
+})
+// 展开与折叠
+const isActive = (name: any) => {
+  isContainerChecked.value = false
+  isMilestoneChecked.value = false
+  isETDChangeChecked.value = false
+  isDepartureChecked.value = false
+  isActiveCollapse.value.ETDChange = false
+  isActiveCollapse.value.Milestone = false
+  isActiveCollapse.value.Departure = false
+  isActiveCollapse.value.Container = false
+  isActiveCollapse.value[name] = true
+}
+const changeCollapse = (val: any) => {
+  if (val == 'Milestone') {
+    isActive('Milestone')
+    isMilestoneChecked.value = true
+  } else if (val == 'Container') {
+    isActive('Container')
+    isContainerChecked.value = true
+  } else if (val == 'Departure') {
+    isActive('Departure')
+    isDepartureChecked.value = true
+  } else if (val == 'ETDChange') {
+    isActive('ETDChange')
+    isETDChangeChecked.value = true
+  } else {
+    isContainerChecked.value = false
+    isMilestoneChecked.value = false
+    isETDChangeChecked.value = false
+    isDepartureChecked.value = false
+    isActiveCollapse.value.ETDChange = false
+    isActiveCollapse.value.Milestone = false
+    isActiveCollapse.value.Departure = false
+    isActiveCollapse.value.Container = false
+  }
+}
+
+const UnsavedCollapse = () => {
+  CollapseActive.value = ''
+}
+
+// 表格列
+const AddedRulesColumns = ref([
+  {
+    field: 'Event',
+    title: 'Event',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'Event Details',
+    title: 'Event Details',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'Frequency',
+    title: 'Frequency',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'Methods',
+    title: 'Methods',
+    type: 'normal',
+    formatter: ''
+  }
+])
+const SubShipmentsColumns = ref([
+  {
+    field: 'h_bol',
+    title: 'HBOL/HAWB',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'shipper',
+    title: 'Shipper',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'consignee',
+    title: 'Consignee',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'etd',
+    title: 'ETD',
+    type: 'normal',
+    formatter: 'date'
+  },
+  {
+    field: 'eta',
+    title: 'ETA',
+    type: 'normal',
+    formatter: 'date'
+  },
+  {
+    field: 'recent_milestone',
+    title: 'Recent Milestone',
+    type: 'normal',
+    formatter: ''
+  }
+])
+
+// System Settings初始数据
+const SubShipmentsTable = ref()
+const AddRulesTable = ref()
+const subscribeInit = ref()
+const getsubscribe = () => {
+  $api.getsubscribe({}).then((res: any) => {
+    if (res.code === 200) {
+      subscribeInit.value = res.data
+      SubShipmentsTable.value.getTableData(res.data.subscribeShipmentWithPage)
+      AddRulesTable.value.getTableData(res.data.addedRules)
+      isMilestoneAdded.value = res.data.Milestone_Update.is_display
+      isContainerAdded.value = res.data.Container_Status_Update.is_display
+      isDepartureAdded.value = res.data['Departure/Arrival_Delay'].is_display
+      isETDChangeAdded.value = res.data['ETD/ETA_Change'].is_display
+    }
+  })
+}
+
+// 保存成功后更改表单数据
+const SavedAddedRules = (val: any, type: any) => {
+  AddRulesTable.value.getTableData(val)
+  CollapseActive.value = ''
+  isMilestoneChecked.value = false
+  isContainerChecked.value = false
+  isDepartureChecked.value = false
+  isETDChangeChecked.value = false
+  if (type == 'Milestone_Update') {
+    isMilestoneAdded.value = true
+  } else if (type == 'Container_Status_Update') {
+    isContainerAdded.value = true
+  } else if (type == 'Arrival_Delay') {
+    isDepartureAdded.value = true
+  } else {
+    isETDChangeAdded.value = true
+  }
+}
+
+// 删除表格后更改数据
+const Milestone = ref()
+const Container = ref()
+const Departure = ref()
+const ETDChange = ref()
+const deleteAddedRules = (val: any) => {
+  if (val == 'Milestone_Update') {
+    isMilestoneAdded.value = false
+    Milestone.value.clearData(val)
+  } else if (val == 'Container_Status_Update') {
+    isContainerAdded.value = false
+    Container.value.clearData(val)
+  } else if (val == 'Departure/Arrival_Delay') {
+    isDepartureAdded.value = false
+    Departure.value.clearData(val)
+  } else {
+    isETDChangeAdded.value = false
+    ETDChange.value.clearData(val)
+  }
+}
+
+// 跳转Create New Rule页面
+const ToCreateRule = () => {
+  router.push({
+    path: '/SystemSettings/createnewrule',
+    query: {}
+  })
+  sessionStorage.setItem('activeTab', 'Monitoring Settings')
+}
+
+const tabledatalength = ref()
+const gettabledatalength = (val: any) => {
+  tabledatalength.value = val
+}
+
+onMounted(() => {
+  getsubscribe()
+  if (sessionStorage.getItem('activeTab') != null) {
+    TabActive.value = sessionStorage.getItem('activeTab')
+    sessionStorage.removeItem('activeTab')
+  }
+})
+</script>
+<template>
+  <div class="Title">System Settings</div>
+  <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="Subscribe Notifications" name="Subscribe Notifications">
+      <div class="subscribedTitle">Notification Events for Subscribed Shipments</div>
+      <div class="SubscribeCollapse">
+        <el-collapse v-model="CollapseActive" accordion @change="changeCollapse">
+          <el-collapse-item name="Milestone" :class="isMilestoneChecked ? 'border_ischecked' : ''">
+            <template #title>
+              <div class="flex">
+                <div class="collapse_left" :class="isMilestoneAdded ? 'text_ischecked' : ''">
+                  <div class="dot" :class="isMilestoneAdded ? 'dot_ischecked' : ''"></div>
+                  Milestone Update
+                </div>
+                <div class="collapse_edit">{{ isMilestoneAdded ? 'Edit' : 'Add' }}</div>
+              </div>
+            </template>
+            <div>
+              <AddRules
+                TitleType="Milestone"
+                @UnsavedCollapse="UnsavedCollapse"
+                :SystemList="subscribeInit"
+                @SavedAddedRules="SavedAddedRules"
+                ref="Milestone"
+              ></AddRules>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item name="Container" :class="isContainerChecked ? 'border_ischecked' : ''">
+            <template #title>
+              <div class="flex">
+                <div class="collapse_left" :class="isContainerAdded ? 'text_ischecked' : ''">
+                  <div class="dot" :class="isContainerAdded ? 'dot_ischecked' : ''"></div>
+                  Container Status Update
+                </div>
+                <div class="collapse_edit">{{ isContainerAdded ? 'Edit' : 'Add' }}</div>
+              </div>
+            </template>
+            <div>
+              <AddRules
+                TitleType="Container"
+                ref="Container"
+                @UnsavedCollapse="UnsavedCollapse"
+                :SystemList="subscribeInit"
+                @SavedAddedRules="SavedAddedRules"
+              ></AddRules>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item name="Departure" :class="isDepartureChecked ? 'border_ischecked' : ''">
+            <template #title>
+              <div class="flex">
+                <div class="collapse_left" :class="isDepartureAdded ? 'text_ischecked' : ''">
+                  <div class="dot" :class="isDepartureAdded ? 'dot_ischecked' : ''"></div>
+                  Departure/Arrival Delay
+                </div>
+                <div class="collapse_edit">{{ isDepartureAdded ? 'Edit' : 'Add' }}</div>
+              </div>
+            </template>
+            <div>
+              <AddRules
+                TitleType="Departure"
+                ref="Departure"
+                @UnsavedCollapse="UnsavedCollapse"
+                :SystemList="subscribeInit"
+                @SavedAddedRules="SavedAddedRules"
+              ></AddRules>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item name="ETDChange" :class="isETDChangeChecked ? 'border_ischecked' : ''">
+            <template #title>
+              <div class="flex">
+                <div class="collapse_left" :class="isETDChangeAdded ? 'text_ischecked' : ''">
+                  <div class="dot" :class="isETDChangeAdded ? 'dot_ischecked' : ''"></div>
+                  ETD/ETA Change
+                </div>
+                <div class="collapse_edit">{{ isETDChangeAdded ? 'Edit' : 'Add' }}</div>
+              </div>
+            </template>
+            <div>
+              <AddRules
+                TitleType="ETDChange"
+                ref="ETDChange"
+                @UnsavedCollapse="UnsavedCollapse"
+                :SystemList="subscribeInit"
+                @SavedAddedRules="SavedAddedRules"
+              ></AddRules>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+      <div class="TableTitle">Added Rules</div>
+      <AddRSettingTableules
+        :propsType="true"
+        ref="AddRulesTable"
+        :ColumnsList="AddedRulesColumns"
+        @deleteAddedRules="deleteAddedRules"
+      ></AddRSettingTableules>
+      <div class="TableTitle subscribetitle">Subscribed Shipments</div>
+      <AddRSettingTableules
+        :ColumnsList="SubShipmentsColumns"
+        ref="SubShipmentsTable"
+      ></AddRSettingTableules>
+    </el-tab-pane>
+    <el-tab-pane label="Monitoring Settings" name="Monitoring Settings">
+      <div class="monitoring_flex">
+        <div class="subscribedTitle">Added Rules</div>
+        <el-button
+          class="el-button--main"
+          style="height: 40px"
+          v-if="tabledatalength != 0 && tabledatalength != null"
+          @click="ToCreateRule"
+          >+ Add Rule</el-button
+        >
+      </div>
+      <MonitoringTable @gettabledatalength="gettabledatalength"></MonitoringTable>
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<style lang="scss" scoped>
+.Title {
+  display: flex;
+  height: 68px;
+  font-size: var(--font-size-6);
+  font-weight: 700;
+  padding: 0 24px;
+  align-items: center;
+}
+.TableTitle {
+  font-size: 18px;
+  font-weight: 700;
+  padding: 21px 24px 13px 24px;
+}
+.subscribetitle {
+  padding-top: 30px;
+}
+.SubscribeCollapse {
+  padding: 0 24px 24px 24px;
+  border-bottom: 1px solid var(--color-select-border);
+}
+:deep(.el-tabs__nav-wrap:after) {
+  height: 2px;
+}
+:deep(.el-tabs__item) {
+  color: var(--color-neutral-1);
+  font-weight: 400;
+  font-size: 18px;
+  padding: 0 64px;
+}
+.subscribedTitle {
+  color: var(--color-neutral-1);
+  font-weight: 700;
+  font-size: 18px;
+  padding: 0 24px;
+  margin: 23px 0 20px 0;
+}
+:deep(.el-collapse) {
+  border: none;
+}
+:deep(.el-collapse-item__wrap) {
+  border: none;
+  background-color: transparent;
+}
+:deep(.el-collapse-item) {
+  border: none;
+  width: 100%;
+  border-radius: 6px;
+  margin: 8px 0;
+}
+:deep(.el-collapse-item__header) {
+  border: 1px solid var(--color-select-border);
+  padding: 0 24px 0 12px;
+  border-radius: 6px;
+  color: var(--color-neutral-1);
+  font-size: 14px;
+  font-weight: 700;
+  height: 64px;
+  display: flex;
+  align-items: center;
+  background-color: transparent;
+}
+.border_ischecked {
+  border: 2px solid var(--color-theme);
+}
+:deep(.el-tabs__nav) {
+  padding-left: 24px;
+}
+:deep(.el-collapse-item__header):hover {
+  background-color: var(--border-hover-color);
+  border-color: var(--border-hover-color);
+}
+:deep(.el-collapse-item__header.is-active) {
+  background-color: var(--border-hover-color);
+  border-color: var(--border-hover-color);
+}
+.flex {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 98%;
+  margin-right: 8px;
+}
+.collapse_left {
+  display: flex;
+  align-items: center;
+}
+.dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: var(--color-dot-unchecked);
+  margin: 0 15px 0 9px;
+}
+.dot_ischecked {
+  background-color: var(--color-dot-checked);
+}
+.text_ischecked {
+  color: var(--color-theme);
+}
+.collapse_edit {
+  color: var(--color-theme);
+  font-weight: 400;
+}
+:deep(.el-collapse-item__arrow) {
+  width: 16px;
+  height: 16px;
+  background-image: url('../src/images/icon_expand_colorful.png');
+  background-size: contain;
+  background-repeat: no-repeat;
+  transform: rotate(0);
+}
+:deep(.el-collapse-item__arrow.is-active) {
+  transform: rotate(-180deg);
+}
+:deep(.el-icon svg) {
+  width: 0;
+}
+.monitoring_flex {
+  display: flex;
+  justify-content: space-between;
+  padding-right: 24px;
+  align-items: end;
+}
+.el-button--main {
+  margin-bottom: 4px;
+}
+</style>

+ 1 - 0
src/views/SystemSettings/src/components/CreateNewrule/index.ts

@@ -0,0 +1 @@
+export { default } from './src/CreateNewrule.vue'

+ 275 - 0
src/views/SystemSettings/src/components/CreateNewrule/src/CreateNewrule.vue

@@ -0,0 +1,275 @@
+<script lang="ts" setup>
+import { ref, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+
+const notificationvalue = ref()
+const notificationoptions = ref([
+  {
+    label: 'Milestone Update',
+    value: 'Milestone'
+  },
+  {
+    label: 'Container Status Update',
+    value: 'Container'
+  },
+  {
+    label: 'Departure/Arrival Delay',
+    value: 'Departure'
+  },
+  {
+    label: 'ETD/ETA Change',
+    value: 'ETDChange'
+  }
+])
+const CancelRulesVisible = ref()
+const ChangeRulesVisible = ref()
+// 保存
+const CreateAddRulesRef = ref()
+const saveMonitoring = () => {
+  CreateAddRulesRef.value.Savesubscribe()
+}
+let select: any = ''
+const changenotification = (val: any) => {
+  select = val
+  if (notificationvalue.value != undefined) {
+    ChangeRulesVisible.value = true
+  }
+}
+const ChangeRulesVisibleOK = () => {
+  ChangeRulesVisible.value = false
+  notificationvalue.value = select
+  CreateAddRulesRef.value.clearData(select)
+}
+const systemList = ref()
+onMounted(() => {
+  console.log(sessionStorage.getItem('CreateAddRulesSystemlist'))
+  if (sessionStorage.getItem('CreateAddRulesSystemlist') == null) {
+    systemList.value = {}
+  } else {
+    systemList.value =
+      JSON.parse(sessionStorage.getItem('CreateAddRulesSystemlist') as string) || {}
+    setTimeout(() => {
+      sessionStorage.removeItem('CreateAddRulesSystemlist')
+    }, 3000)
+  }
+  console.log(systemList.value)
+})
+</script>
+<template>
+  <div class="Title">
+    <div>Create New Rule</div>
+    <div>
+      <el-button class="create_button" type="default" @click="CancelRulesVisible = true">
+        <span class="iconfont_icon">
+          <svg class="iconfont" aria-hidden="true">
+            <use xlink:href="#icon-icon_return_b"></use>
+          </svg>
+        </span>
+        Cancel
+      </el-button>
+      <!-- 取消保存 -->
+      <el-dialog v-model="CancelRulesVisible" width="480">
+        <div style="font-weight: 400">You have unsaved changes.</div>
+        <div style="font-weight: 400">Are you sure you want to leave this page?</div>
+        <template #footer>
+          <div class="dialog-footer">
+            <el-button type="default" @click="CancelRulesVisible = false" style="width: 100px"
+              >Cancel</el-button
+            >
+            <el-button class="el-button--warning" @click="router.back()" style="width: 100px">
+              OK
+            </el-button>
+          </div>
+        </template>
+        <template #header>
+          <div class="cancel_header">
+            <span class="iconfont_icon iconfont_warning">
+              <svg class="iconfont icon_warning" aria-hidden="true">
+                <use xlink:href="#icon-icon_tipsfilled_b"></use>
+              </svg>
+            </span>
+            Unsaved Changes
+          </div>
+        </template>
+      </el-dialog>
+      <!-- 切换select -->
+      <el-dialog v-model="ChangeRulesVisible" width="480">
+        <div style="font-weight: 400">You have unsaved changes.</div>
+        <div style="font-weight: 400">Are you sure you want to leave this page?</div>
+        <template #footer>
+          <div class="dialog-footer">
+            <el-button type="default" @click="ChangeRulesVisible = false" style="width: 100px"
+              >Cancel</el-button
+            >
+            <el-button
+              class="el-button--warning"
+              @click="ChangeRulesVisibleOK"
+              style="width: 100px"
+            >
+              OK
+            </el-button>
+          </div>
+        </template>
+        <template #header>
+          <div class="cancel_header">
+            <span class="iconfont_icon iconfont_warning">
+              <svg class="iconfont icon_warning" aria-hidden="true">
+                <use xlink:href="#icon-icon_tipsfilled_b"></use>
+              </svg>
+            </span>
+            Unsaved Changes
+          </div>
+        </template>
+      </el-dialog>
+      <el-button
+        :disabled="notificationvalue == '' || notificationvalue == undefined"
+        class="el-button--main create_button"
+        @click="saveMonitoring"
+      >
+        <span class="iconfont_icon">
+          <svg class="iconfont" aria-hidden="true">
+            <use xlink:href="#icon-icon_save_b"></use>
+          </svg>
+        </span>
+        Save
+      </el-button>
+    </div>
+  </div>
+  <div class="contetnt">
+    <div class="notification">
+      <div class="notification_title">Notification Events</div>
+      <div>
+        <el-select v-model="notificationvalue" placeholder="Select event" style="width: 400px">
+          <el-option
+            v-for="item in notificationoptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+            @mousedown="changenotification(item.value)"
+          />
+        </el-select>
+      </div>
+    </div>
+    <div class="setting">
+      <div class="setting_header">Setting</div>
+      <TableEmpty
+        v-if="notificationvalue == '' || notificationvalue == undefined"
+        style="margin-top: 100px"
+        EmptyTitle="Please select Notification Events first"
+      ></TableEmpty>
+      <div v-else>
+        {{ systemList }}
+        <CreateAddRules
+          :SystemList="systemList"
+          ref="CreateAddRulesRef"
+          :TitleType="notificationvalue"
+        ></CreateAddRules>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.Title {
+  display: flex;
+  background-color: var(--color-mode);
+  height: 68px;
+  font-size: var(--font-size-6);
+  font-weight: 700;
+  padding: 0 24px;
+  align-items: center;
+  justify-content: space-between;
+  border-bottom: 1px solid var(--color-border);
+}
+.create_button {
+  width: 115px;
+  height: 40px;
+}
+.contetnt {
+  padding: 0 24px;
+  margin-top: 16px;
+  margin-bottom: 14px;
+}
+.notification {
+  border: 1px solid var(--color-border);
+  padding: 16px;
+  border-radius: 12px;
+}
+.notification_title {
+  font-size: 18px;
+  font-weight: 700;
+  margin-bottom: 21px;
+}
+.setting {
+  margin-top: 8px;
+  border: 1px solid var(--color-border);
+  border-radius: 12px;
+  min-height: 741px;
+}
+.setting_header {
+  font-size: 18px;
+  font-weight: 700;
+  background-color: var(--color-dialog-header-bg);
+  height: 48px;
+  padding: 13px 16px;
+  border-radius: 12px 12px 0 0;
+}
+:deep(.el-collapse-item__header):hover {
+  background-color: var(--border-hover-color);
+  border-color: var(--border-hover-color);
+}
+:deep(.el-collapse-item__header.is-active) {
+  background-color: var(--border-hover-color);
+  border-color: var(--border-hover-color);
+}
+:deep(.el-collapse) {
+  border: none;
+}
+:deep(.el-collapse-item__wrap) {
+  border: none;
+  background-color: transparent;
+}
+:deep(.el-collapse-item) {
+  border: none;
+  width: 100%;
+  border-radius: 6px;
+  margin: 8px 0;
+}
+:deep(.el-collapse-item__header) {
+  border: 1px solid var(--color-select-border);
+  padding: 0 24px 0 12px;
+  border-radius: 6px;
+  color: var(--color-neutral-1);
+  font-size: 14px;
+  font-weight: 700;
+  height: 64px;
+  display: flex;
+  align-items: center;
+  background-color: transparent;
+}
+.cancel_header {
+  font-size: 18px;
+  font-weight: 700;
+  color: var(--color-neutral-1);
+  display: flex;
+  align-items: center;
+}
+.icon_warning {
+  width: 22px;
+  height: 22px;
+  margin-right: 0;
+  fill: var(--color-btn-warning-bg);
+}
+.iconfont_warning {
+  display: flex;
+  align-items: center;
+}
+:deep(header.el-dialog__header) {
+  background-color: #fff;
+}
+:deep(footer.el-dialog__footer) {
+  border-top: none;
+}
+</style>

+ 1 - 0
src/views/SystemSettings/src/components/MonitoringTable/index.ts

@@ -0,0 +1 @@
+export { default } from './src/MonitoringTable.vue'

+ 261 - 0
src/views/SystemSettings/src/components/MonitoringTable/src/MonitoringTable.vue

@@ -0,0 +1,261 @@
+<script setup lang="ts">
+import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+import { useRowClickStyle } from '@/hooks/rowClickStyle'
+import dayjs from 'dayjs'
+import { ref, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+
+const emits = defineEmits(['gettabledatalength'])
+
+const columnstest = ref([
+  {
+    field: '_rules_type_display',
+    title: 'Event',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'shipment_details',
+    title: 'Shipment Range',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'event_details',
+    title: 'Event Details',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'frequency_display',
+    title: 'Frequency',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'method_display',
+    title: 'Methods',
+    type: 'normal',
+    formatter: ''
+  }
+])
+const datatest = ref([])
+const tableData = ref<VxeGridProps<any>>({
+  border: true,
+  round: true,
+  minHeight: 700,
+  maxHeight: 700,
+  columns: [],
+  data: [],
+  scrollY: { enabled: true, oSize: 20, gt: 30 },
+  stripe: true,
+  emptyText: ' ',
+  showHeaderOverflow: true,
+  showOverflow: true,
+  headerRowStyle: {
+    backgroundColor: 'var(--color-table-header-bg)'
+  },
+  columnConfig: { resizable: true, useKey: true },
+  rowConfig: { isHover: true }
+})
+
+const tableRef = ref<VxeGridInstance | null>(null)
+const pageInfo = ref({ pageNo: 1, pageSize: 15, total: 0 })
+
+const handleColumns = (columns: any) => {
+  const newColumns = columns.map((item: any) => {
+    let curColumn: any = {
+      title: item.title,
+      field: item.field
+    }
+    // 格式化
+    if (item.formatter === 'date') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) =>
+          cellValue ? dayjs(cellValue).format('MMM-DD-YYYY ') : '--'
+      }
+    }
+    return curColumn
+  })
+  return newColumns
+}
+// 获取表格列
+const getTableColumns = async () => {
+  tableData.value.columns = handleColumns(columnstest.value)
+  tableData.value.columns?.push({
+    title: 'Operation',
+    fixed: 'right',
+    width: 100,
+    slots: { default: 'action' }
+  })
+}
+// 获取表格数据
+const getTableData = async () => {
+  // 保存页长以及当前页码
+  tableData.value.data = datatest.value
+  $api
+    .MonitoringTable({
+      cp: pageInfo.value.pageNo,
+      ps: pageInfo.value.pageSize
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        pageInfo.value.total = Number(res.data.rc)
+        pageInfo.value.pageNo = res.data.cp
+        pageInfo.value.pageSize = res.data.ps
+        tableData.value.data = res.data.monitoringRules
+        emits('gettabledatalength', res.data.monitoringRules.length)
+      }
+    })
+    .finally(() => {})
+}
+
+// 实现行点击样式
+useRowClickStyle(tableRef)
+
+// 点击删除
+const handleDelete = (row: any) => {
+  row.visible = true
+}
+
+// 跳转Create New Rule页面
+const ToCreateRule = () => {
+  router.push({
+    path: '/SystemSettings/createnewrule',
+    query: {}
+  })
+  sessionStorage.setItem('activeTab', 'Monitoring Settings')
+}
+
+// 删除表格数据
+const deleteMoniTable = (row: any) => {
+  row.visible = false
+  $api
+    .deleteMonitoringTable({
+      id: row.id
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        tableData.value.data = tableData.value.data?.filter((item) => item.id !== row.id)
+        emits('gettabledatalength', tableData.value.data?.length)
+      }
+    })
+}
+
+// 编辑表格数据
+const handleedit = (row: any) => {
+  $api
+    .EditMonitoringTable({
+      id: row.id,
+      rules_type: row.rules_type
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        router.push({
+          path: '/SystemSettings/createnewrule',
+          query: {}
+        })
+        sessionStorage.setItem('activeTab', 'Monitoring Settings')
+        sessionStorage.setItem('CreateAddRulesSystemlist', JSON.stringify(res.data[row.rules_type]))
+      }
+    })
+}
+
+onMounted(() => {
+  getTableColumns()
+  getTableData()
+})
+</script>
+
+<template>
+  <div class="SettingTable">
+    <vxe-grid ref="tableRef" :style="{ border: 'none' }" v-bind="tableData">
+      <!-- 空数据时的插槽 -->
+      <template #empty v-if="tableData.data.length === 0">
+        <TableEmpty EmptyTitle="Customize your shipment tracking preferences.">
+          <template #suggestion>
+            <el-button class="el-button--main" @click="ToCreateRule">+ Add Rule</el-button>
+          </template>
+        </TableEmpty>
+      </template>
+      <template #action="{ row }">
+        <el-button class="el-button--blue" style="height: 24px" @click="handleedit(row)">
+          <span class="font_family icon-icon_edit_b"></span>
+        </el-button>
+        <el-popover trigger="click" :visible="row.visible" placement="left" :width="480">
+          <div class="delete_title">
+            <span class="font_family icon_alert icon-icon_tipsfilled_b"></span>
+            Delete Rules
+          </div>
+          <p class="delete_content">Are you sure to delete this notification event?</p>
+          <div style="text-align: right; margin: 0; padding: 8px">
+            <el-button style="width: 100px" class="el-button--default" @click="row.visible = false"
+              >cancel</el-button
+            >
+            <el-button style="width: 100px" type="warning" @click="deleteMoniTable(row)">
+              OK
+            </el-button>
+          </div>
+          <template #reference>
+            <el-button @click="handleDelete(row)" class="el-button--blue" style="height: 24px">
+              <span class="font_family icon-icon_delete_b"></span>
+            </el-button>
+          </template>
+        </el-popover>
+      </template>
+    </vxe-grid>
+  </div>
+  <div class="pagination">
+    <span>Total {{ pageInfo.total }}</span>
+    <el-pagination
+      v-model:current-page="pageInfo.pageNo"
+      v-model:page-size="pageInfo.pageSize"
+      layout="prev, pager, next"
+      :total="pageInfo.total"
+      :pager-count="5"
+      @size-change="getTableData"
+      @current-change="getTableData"
+      background
+    />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.SettingTable {
+  padding: 0 24px 0 24px;
+}
+.icon-icon_delete_b::before {
+  color: var(--color-btn-danger-bg);
+}
+.icon_alert::before {
+  color: var(--color-btn-warning-bg);
+}
+.delete_title {
+  font-size: 18px;
+  font-weight: 700;
+  padding: 20px 16px;
+  color: var(--color-neutral-1);
+}
+.delete_content {
+  font-size: 14px;
+  font-weight: 400;
+  color: var(--color-neutral-1);
+  padding: 15px 0 33px 37px;
+}
+.pagination {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border: 1px solid var(--color-border);
+  border-top: none;
+  margin: 0 24px 0 24px;
+  padding: 4px 8px;
+  border-radius: 0 0 6px 6px;
+}
+:deep(.el-icon svg) {
+  width: 1em !important;
+}
+</style>

+ 1 - 0
src/views/SystemSettings/src/components/SettingTable/index.ts

@@ -0,0 +1 @@
+export { default } from './src/SettingTable.vue'

+ 212 - 0
src/views/SystemSettings/src/components/SettingTable/src/SettingTable.vue

@@ -0,0 +1,212 @@
+<script setup lang="ts">
+import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+import { useRowClickStyle } from '@/hooks/rowClickStyle'
+import dayjs from 'dayjs'
+import { ref, onMounted } from 'vue'
+interface ColumnsListItem {
+  field: String
+  title: String
+  type: String
+  formatter: String
+}
+const props = defineProps({
+  propsType: Boolean,
+  ColumnsList: Array<ColumnsListItem>
+})
+
+const columnstest = ref(props.ColumnsList)
+
+const tableData = ref<VxeGridProps<any>>({
+  border: true,
+  round: true,
+  minHeight: 70,
+  maxHeight: 500,
+  columns: [],
+  data: [],
+  scrollY: { enabled: true, oSize: 20, gt: 30 },
+  stripe: true,
+  emptyText: ' ',
+  showHeaderOverflow: true,
+  showOverflow: true,
+  headerRowStyle: {
+    backgroundColor: 'var(--color-table-header-bg)'
+  },
+  columnConfig: { resizable: true, useKey: true },
+  rowConfig: { isHover: true }
+})
+const pageInfo = ref({ pageNo: 1, pageSize: 10, total: 0 })
+
+const tableRef = ref<VxeGridInstance | null>(null)
+
+const handleColumns = (columns: any) => {
+  const newColumns = columns.map((item: any) => {
+    let curColumn: any = {
+      title: item.title,
+      field: item.field
+    }
+    // 格式化
+    if (item.formatter === 'date') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) =>
+          cellValue ? dayjs(cellValue).format('MMM-DD-YYYY ') : '--'
+      }
+    }
+    return curColumn
+  })
+  return newColumns
+}
+// 获取表格列
+const getTableColumns = async () => {
+  tableData.value.columns = handleColumns(columnstest.value)
+  if (props.propsType) {
+    tableData.value.columns?.push({
+      title: 'Action',
+      fixed: 'right',
+      width: 80,
+      slots: { default: 'action' }
+    })
+  }
+}
+// 获取表格数据
+const getTableData = (val: any) => {
+  tableData.value.data = val.tableData
+  if (!props.propsType) {
+    pageInfo.value.pageNo = val.cp
+    pageInfo.value.pageSize = val.ps
+    pageInfo.value.total = val.rc
+  }
+}
+
+// 实现行点击样式
+useRowClickStyle(tableRef)
+
+const emits = defineEmits(['deleteAddedRules'])
+// 点击删除
+const handleDelete = (row: any) => {
+  row.visible = false
+  $api
+    .DeleteAddedRules({
+      rules_type: row.Event
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        tableData.value.data = tableData.value.data?.filter((item) => item.Event !== row.Event)
+        emits('deleteAddedRules', row.Event)
+      }
+    })
+}
+
+onMounted(() => {
+  getTableColumns()
+})
+defineExpose({
+  getTableData
+})
+const getpaginationTableData = () => {
+  $api
+    .SubscribePagination({
+      cp: pageInfo.value.pageNo,
+      ps: pageInfo.value.pageSize
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        pageInfo.value.total = Number(res.data.rc)
+        pageInfo.value.pageNo = res.data.cp
+        pageInfo.value.pageSize = res.data.ps
+        tableData.value.data = res.data.tableData
+      }
+    })
+}
+</script>
+
+<template>
+  <div class="SettingTable">
+    <vxe-grid
+      :class="props.propsType ? 'radius-bottom' : ''"
+      ref="tableRef"
+      :style="{ border: 'none' }"
+      v-bind="tableData"
+    >
+      <template #empty>
+        <div class="empty">No data</div>
+      </template>
+
+      <template #action="{ row }">
+        <el-popover :visible="row.visible" placement="left" :width="480">
+          <div class="delete_title">
+            <span class="font_family icon_alert icon-icon_tipsfilled_b"></span>
+            Delete Rules
+          </div>
+          <p class="delete_content">Are you sure to delete this notification event?</p>
+          <div style="text-align: right; margin: 0; padding: 8px">
+            <el-button style="width: 100px" tupe="default" @click="row.visible = false"
+              >cancel</el-button
+            >
+            <el-button style="width: 100px" type="warning" @click="handleDelete(row)">
+              OK
+            </el-button>
+          </div>
+          <template #reference>
+            <el-button @click="row.visible = true" class="el-button--blue" style="height: 24px">
+              <span class="font_family icon-icon_delete_b"></span>
+            </el-button>
+          </template>
+        </el-popover>
+      </template>
+    </vxe-grid>
+  </div>
+  <div class="pagination" v-if="!props.propsType">
+    <span>Total {{ pageInfo.total }}</span>
+    <el-pagination
+      v-model:current-page="pageInfo.pageNo"
+      v-model:page-size="pageInfo.pageSize"
+      layout="prev, pager, next"
+      :total="pageInfo.total"
+      :pager-count="5"
+      @size-change="getpaginationTableData"
+      @current-change="getpaginationTableData"
+      background
+    />
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.SettingTable {
+  padding: 0 24px 0 24px;
+}
+.radius-bottom {
+  border-radius: 6px 6px 0 0;
+}
+.font_family::before {
+  color: var(--color-btn-danger-bg);
+}
+.icon_alert::before {
+  color: var(--color-btn-warning-bg);
+}
+.delete_title {
+  font-size: 18px;
+  font-weight: 700;
+  padding: 20px 16px;
+  color: var(--color-neutral-1);
+}
+.delete_content {
+  font-size: 14px;
+  font-weight: 400;
+  color: var(--color-neutral-1);
+  padding: 15px 0 33px 37px;
+}
+.pagination {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border: 1px solid var(--color-border);
+  border-top: none;
+  margin: 0 24px 20px 24px;
+  padding: 4px 8px;
+  border-radius: 0 0 6px 6px;
+}
+:deep(.el-icon svg) {
+  width: 1em !important;
+}
+</style>

BIN
src/views/SystemSettings/src/images/icon_collapse_colorful.png


BIN
src/views/SystemSettings/src/images/icon_expand_colorful.png


BIN
src/views/SystemSettings/src/images/xia.png


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

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import { ref } from 'vue'
 import { VueDraggable } from 'vue-draggable-plus'
 import BasicInformation from './components/BasicInformation.vue'
 import ContainersView from './components/ContainersView.vue'
@@ -87,6 +88,8 @@ const handleAMSISF = () => {
 
 const allData = ref()
 const loading = ref(false)
+const subscribe_a = ref()
+const is_subscribe = ref(false)
 const getData = () => {
   loading.value = true
   $api
@@ -96,6 +99,8 @@ const getData = () => {
     })
     .then((res: any) => {
       if (res.code === 200) {
+        subscribe_a.value = res.data.a
+        is_subscribe.value = res.data.is_subscribe
         // 获取数据
         allData.value = res.data
       }
@@ -115,6 +120,19 @@ const dialogVModel = ref(false)
 const openShareDialog = () => {
   dialogVModel.value = true
 }
+const SubscribeShipments = () => {
+  is_subscribe.value = !is_subscribe.value
+  $api
+    .SubscribeShipments({
+      serial_no: subscribe_a.value,
+      is_subscribe: is_subscribe.value
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        console.log(res.data)
+      }
+    })
+}
 </script>
 
 <template>
@@ -148,6 +166,24 @@ const openShareDialog = () => {
             <span class="font_family icon-icon_log_b" style="margin-right: 4px"></span
             >AMS/ISF</el-button
           >
+
+          <el-button
+            class="recent_button"
+            @click="SubscribeShipments"
+            :class="is_subscribe ? 'IsSubscribe' : ''"
+          >
+            <span v-if="is_subscribe" class="iconfont_icon">
+              <svg class="iconfont" aria-hidden="true">
+                <use xlink:href="#icon-icon_marked_b"></use>
+              </svg>
+            </span>
+            <span v-else class="iconfont_icon">
+              <svg class="iconfont" aria-hidden="true">
+                <use xlink:href="#icon-icon_unmark_b"></use>
+              </svg>
+            </span>
+            <span class="Subscribe">Subscribe</span>
+          </el-button>
         </div>
       </div>
       <div class="detail-info">
@@ -513,6 +549,27 @@ const openShareDialog = () => {
   overflow: hidden; /* 隐藏超出部分 */
   text-overflow: ellipsis; /* 超出部分显示省略号 */
 }
+
+.recent_button {
+  background-color: var(--color-white);
+  border: 1px solid var(--color-border);
+  &:hover {
+    border: 1px solid var(--color-btn-main-plain-bg-hover);
+    background-color: var(--color-btn-main-plain-bg-hover);
+    fill: var(--color-theme);
+    .Subscribe {
+      color: var(--color-theme);
+    }
+  }
+}
+.IsSubscribe {
+  border: 1px solid var(--color-theme);
+  background-color: var(--color-theme);
+  fill: white;
+  .Subscribe {
+    color: white;
+  }
+}
 </style>
 <style lang="scss">
 .is-show-tooltip {

+ 56 - 2
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -87,7 +87,7 @@ const getTableColumns = async () => {
         trackingTable.value.columns.push({
           title: 'Action',
           fixed: 'right',
-          width: 80,
+          width: 120,
           slots: { default: 'action' }
         })
       }
@@ -131,7 +131,7 @@ const assignTableData = (data: any) => {
       trackingTable.value.columns.push({
         title: 'Action',
         fixed: 'right',
-        width: 80,
+        width: 120,
         slots: { default: 'action' }
       })
     }
@@ -546,6 +546,21 @@ const handleRowClassName = ({ row }: any) => {
   return ''
 }
 
+// 点击订阅
+const SubscribeShipments = (row: any) => {
+  row.is_subscribe = !row.is_subscribe
+  $api
+    .SubscribeShipments({
+      serial_no: row.a,
+      is_subscribe: row.is_subscribe
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        console.log(res.data)
+      }
+    })
+}
+
 defineExpose({
   searchTableData,
   getSharedTableData,
@@ -615,6 +630,23 @@ defineExpose({
           <span class="font_family icon-icon_vgm_b"></span>
           <span style="font-size: 12px">VGM</span>
         </el-button>
+
+        <el-button
+          class="recent_button el-button--blue"
+          @click="SubscribeShipments(row)"
+          :class="row.is_subscribe ? 'IsSubscribe' : ''"
+        >
+          <span v-if="row.is_subscribe" class="iconfont_icon">
+            <svg class="iconfont" aria-hidden="true">
+              <use xlink:href="#icon-icon_marked_b"></use>
+            </svg>
+          </span>
+          <span v-else class="iconfont_icon">
+            <svg class="iconfont" aria-hidden="true">
+              <use xlink:href="#icon-icon_unmark_b"></use>
+            </svg>
+          </span>
+        </el-button>
       </template>
       <!-- Transportation Mode字段的插槽 -->
       <template #mode="{ row, column }">
@@ -706,4 +738,26 @@ defineExpose({
     width: 20px;
   }
 }
+.recent_button {
+  height: 24px;
+  padding: 6px 3px 6px 6px;
+  .Subscribe {
+    color: var(--color-theme);
+  }
+  &:hover {
+    background-color: var(--color-btn-main-plain-bg-hover);
+    fill: var(--color-theme);
+    .Subscribe {
+      color: var(--color-theme);
+    }
+  }
+}
+.IsSubscribe {
+  border: 1px solid var(--color-theme);
+  background-color: var(--color-theme);
+  fill: white;
+  .Subscribe {
+    color: white;
+  }
+}
 </style>

+ 1 - 0
vite.config.ts

@@ -10,6 +10,7 @@ import IconsResolver from 'unplugin-icons/resolver'
 // https://vitejs.dev/config/
 export default defineConfig({
   base: `/`,
+  // base: `/k_new_online/`,
   resolve: {
     alias: {
       '@': fileURLToPath(new URL('./src', import.meta.url))

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff