瀏覽代碼

Merge branch 'test_zyh' of United_Software/k_online_ui into test

Jack Zhou 2 月之前
父節點
當前提交
e5950e3e42
共有 31 個文件被更改,包括 1294 次插入567 次删除
  1. 1 1
      .env.development
  2. 1 1
      package.json
  3. 16 0
      src/api/module/login.ts
  4. 0 1
      src/components/AIRobot/src/AIRobot.vue
  5. 118 66
      src/components/CreateAddRules/src/components/ShipmentRange.vue
  6. 7 3
      src/components/DateRange/src/components/CalendarDate.vue
  7. 2 1
      src/stores/modules/user.ts
  8. 3 0
      src/styles/Antdui.scss
  9. 2 1
      src/styles/elementui.scss
  10. 9 0
      src/styles/theme.scss
  11. 3 0
      src/views/AIApiLog/src/components/LogDialog.vue
  12. 2 1
      src/views/AIRobotChat/src/components/AIQuestions.vue
  13. 4 0
      src/views/Booking/src/components/BookingDetail/src/components/EmailView.vue
  14. 1 0
      src/views/ChatLog/src/components/TableView/src/TableView.vue
  15. 5 5
      src/views/DestinationDelivery/src/DestinationDelivery.vue
  16. 28 34
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue
  17. 17 90
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/RecommendDate.vue
  18. 1 1
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectStation.vue
  19. 61 9
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue
  20. 404 252
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue
  21. 2 1
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/components/NewbookingTable.vue
  22. 42 5
      src/views/DestinationDelivery/src/components/DeliveryDate.vue
  23. 28 5
      src/views/DestinationDelivery/src/components/TableView/src/TableView.vue
  24. 4 0
      src/views/DestinationDelivery/src/components/TableView/src/components/EmailDialog.vue
  25. 75 75
      src/views/Layout/src/components/Menu/MenuView.vue
  26. 97 0
      src/views/Login/src/components/ActivatedDialog.vue
  27. 328 0
      src/views/Login/src/components/SetPasswordDialog.vue
  28. 19 2
      src/views/Login/src/loginView.vue
  29. 8 11
      src/views/SystemSettings/src/components/MonitoringTable/src/MonitoringTable.vue
  30. 4 0
      src/views/Tracking/src/components/TrackingDetail/src/components/EmailDrawer.vue
  31. 2 2
      vite.config.ts

+ 1 - 1
.env.development

@@ -1,2 +1,2 @@
-VITE_API_HOST = 'http://192.168.0.161/Customer_Service_Online'
+VITE_API_HOST = 'http://192.168.14.224/Customer_Service_Online'
 VITE_BASE_URL = '/k_new_online/'

+ 1 - 1
package.json

@@ -40,7 +40,7 @@
     "moment": "^2.30.1",
     "moment-timezone": "^0.5.46",
     "pinia": "^2.2.2",
-    "sass-loader": "^16.0.2",
+    "sass-loader": "^14.1.1",
     "vue": "^3.4.29",
     "vue-draggable-plus": "^0.5.3",
     "vue-json-pretty": "^2.5.0",

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

@@ -91,6 +91,22 @@ export const changePassword = (params: any, config: any) => {
   )
 }
 
+/**
+ * 重置和激活密码
+ */
+export const resetAndActivatePassword = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'login',
+      operate: 'resetAndActivateUpdate',
+      ...params
+    },
+    config
+  )
+}
+
+
 /**
  * 获取public tracking detail详情数据
  */

+ 0 - 1
src/components/AIRobot/src/AIRobot.vue

@@ -129,7 +129,6 @@ onMounted(() => {
   if(localStorage.getItem('userInfo') != null) {
     AIIconVisible.value = true
   }
-  AIRobotInit()
   emitter.on('login-success', isShowLogin)
   emitter.on('login-out', Logout)
   emitter.on('checkPrompt', Logout)

+ 118 - 66
src/components/CreateAddRules/src/components/ShipmentRange.vue

@@ -44,13 +44,19 @@ const ShipmentRangeInit = () => {
     TransportCheckedList.value = ShipmentRange_data.value?.shipment_transport_mode.split(';')
     CheckChange(ShipmentRange_data.value?.shipment_transport_mode)
   }
-  if (typeof ShipmentRange_data.value?.shipment_eta_limit == 'number' && typeof ShipmentRange_data.value?.shipment_eta_limit_from == 'number') {
+  if (
+    typeof ShipmentRange_data.value?.shipment_eta_limit == 'number' &&
+    typeof ShipmentRange_data.value?.shipment_eta_limit_from == 'number'
+  ) {
     ETATimeStart.value = ShipmentRange_data.value?.shipment_eta_limit_from
     ETATimeEnd.value = ShipmentRange_data.value?.shipment_eta_limit
     TimeChecked.value = 2
     changeTime(2)
   }
-  if (typeof ShipmentRange_data.value?.shipment_etd_limit == 'number' && typeof ShipmentRange_data.value?.shipment_etd_limit_from == 'number') {
+  if (
+    typeof ShipmentRange_data.value?.shipment_etd_limit == 'number' &&
+    typeof ShipmentRange_data.value?.shipment_etd_limit_from == 'number'
+  ) {
     ETDTimeStart.value = ShipmentRange_data.value?.shipment_etd_limit_from
     ETDTimeEnd.value = ShipmentRange_data.value?.shipment_etd_limit
     TimeChecked.value = 1
@@ -67,56 +73,56 @@ const clampedETDValueStart = computed({
   get: () => ETDTimeStart.value,
   set: (newVal) => {
     // 转换为整数
-    const num = parseInt(newVal, 10);
+    const num = parseInt(newVal, 10)
     // 处理非数字和NaN情况
     if (isNaN(num)) {
-      ETDTimeStart.value = 0;
-      return;
+      ETDTimeStart.value = 0
+      return
     }
     // 范围限制
-    ETDTimeStart.value = Math.max(0, Math.min(365, num));
+    ETDTimeStart.value = Math.max(0, Math.min(365, num))
   }
 })
 const clampedETDValueEnd = computed({
   get: () => ETDTimeEnd.value,
   set: (newVal) => {
     // 转换为整数
-    const num = parseInt(newVal, 10);
+    const num = parseInt(newVal, 10)
     // 处理非数字和NaN情况
     if (isNaN(num)) {
-      ETDTimeEnd.value = 0;
-      return;
+      ETDTimeEnd.value = 0
+      return
     }
     // 范围限制
-    ETDTimeEnd.value = Math.max(0, Math.min(365, num));
+    ETDTimeEnd.value = Math.max(0, Math.min(365, num))
   }
 })
 const clampedETAValueStart = computed({
   get: () => ETATimeStart.value,
   set: (newVal) => {
     // 转换为整数
-    const num = parseInt(newVal, 10);
+    const num = parseInt(newVal, 10)
     // 处理非数字和NaN情况
     if (isNaN(num)) {
-      ETATimeStart.value = 0;
-      return;
+      ETATimeStart.value = 0
+      return
     }
     // 范围限制
-    ETATimeStart.value = Math.max(0, Math.min(365, num));
+    ETATimeStart.value = Math.max(0, Math.min(365, num))
   }
 })
 const clampedETAValueEnd = computed({
   get: () => ETATimeEnd.value,
   set: (newVal) => {
     // 转换为整数
-    const num = parseInt(newVal, 10);
+    const num = parseInt(newVal, 10)
     // 处理非数字和NaN情况
     if (isNaN(num)) {
-      ETATimeEnd.value = 0;
-      return;
+      ETATimeEnd.value = 0
+      return
     }
     // 范围限制
-    ETATimeEnd.value = Math.max(0, Math.min(365, num));
+    ETATimeEnd.value = Math.max(0, Math.min(365, num))
   }
 })
 
@@ -139,56 +145,72 @@ const changeTime = (val: any) => {
   if (val == 1) {
     isETDVisible.value = true
     isETAVisible.value = false
-    defaultradio.value =''
-    defaultradio2.value =''
+    defaultradio.value = ''
+    defaultradio2.value = ''
     ETATimeStart.value = ''
     ETATimeEnd.value = ''
-    if(typeof clampedETDValueStart.value == 'number' && typeof clampedETDValueEnd.value == 'number') {
-      if(clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 30) {
+    if (
+      typeof clampedETDValueStart.value == 'number' &&
+      typeof clampedETDValueEnd.value == 'number'
+    ) {
+      if (clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 30) {
         defaultradio.value = 'Next 30 days'
-      } else if(clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 60) {
+      } else if (clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 60) {
         defaultradio.value = 'Next 60 days'
-      } else if(clampedETDValueStart.value == 10 && clampedETDValueEnd.value == 60) {
+      } else if (clampedETDValueStart.value == 10 && clampedETDValueEnd.value == 60) {
         defaultradio.value = 'Past 10 days to next 60 day'
-      } else if(clampedETDValueStart.value == 30 && clampedETDValueEnd.value == 0) {
+      } else if (clampedETDValueStart.value == 30 && clampedETDValueEnd.value == 0) {
         defaultradio.value = 'Past 30 days'
       } else {
         defaultradio.value = 'Customize'
       }
-      if(clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 0) {
+      if (clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 0) {
         Timestr = ''
       } else {
-        Timestr = 'ETD: minus ' + clampedETDValueStart.value + ' Day(s) to Plus '+ clampedETDValueEnd.value + ' Day(s)'
+        Timestr =
+          'ETD: minus ' +
+          clampedETDValueStart.value +
+          ' Day(s) to Plus ' +
+          clampedETDValueEnd.value +
+          ' Day(s)'
       }
-    } else{
+    } else {
       Timestr = ''
     }
-    emit('ChangeCheckTimeRules', Timestr, clampedETDValueStart.value,clampedETDValueEnd.value)
+    emit('ChangeCheckTimeRules', Timestr, clampedETDValueStart.value, clampedETDValueEnd.value)
   } else if (val == 2) {
     isETDVisible.value = false
     isETAVisible.value = true
-    defaultradio2.value =''
-    defaultradio.value =''
+    defaultradio2.value = ''
+    defaultradio.value = ''
     ETDTimeStart.value = ''
     ETDTimeEnd.value = ''
-    if(typeof clampedETAValueStart.value == 'number' && typeof clampedETAValueEnd.value == 'number') {
-      if(clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 30) {
+    if (
+      typeof clampedETAValueStart.value == 'number' &&
+      typeof clampedETAValueEnd.value == 'number'
+    ) {
+      if (clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 30) {
         defaultradio2.value = 'Next 30 days'
-      } else if(clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 60) {
+      } else if (clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 60) {
         defaultradio2.value = 'Next 60 days'
-      } else if(clampedETAValueStart.value == 10 && clampedETAValueEnd.value == 60) {
+      } else if (clampedETAValueStart.value == 10 && clampedETAValueEnd.value == 60) {
         defaultradio2.value = 'Past 10 days to next 60 day'
-      } else if(clampedETAValueStart.value == 30 && clampedETAValueEnd.value == 0) {
+      } else if (clampedETAValueStart.value == 30 && clampedETAValueEnd.value == 0) {
         defaultradio2.value = 'Past 30 days'
       } else {
         defaultradio2.value = 'Customize'
       }
-      if(clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 0) {
+      if (clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 0) {
         Timestr = ''
       } else {
-        Timestr = 'ETA: minus ' + clampedETAValueStart.value + ' Day(s) to Plus '+ clampedETAValueEnd.value + ' Day(s)'
+        Timestr =
+          'ETA: minus ' +
+          clampedETAValueStart.value +
+          ' Day(s) to Plus ' +
+          clampedETAValueEnd.value +
+          ' Day(s)'
       }
-    } else{
+    } else {
       Timestr = ''
     }
     emit('ChangeCheckTimeRules', Timestr, clampedETAValueStart.value, clampedETAValueEnd.value)
@@ -198,54 +220,64 @@ const changeTime = (val: any) => {
 }
 
 //切换默认值
-const changedefaultradio = (val:any) => {
-  if(val == 'Next 30 days') {
+const changedefaultradio = (val: any) => {
+  if (val == 'Next 30 days') {
     clampedETDValueStart.value = 0
     clampedETDValueEnd.value = 30
-  } else if(val == 'Next 60 days') {
+  } else if (val == 'Next 60 days') {
     clampedETDValueStart.value = 0
     clampedETDValueEnd.value = 60
-  } else if(val == 'Past 30 days') {
+  } else if (val == 'Past 30 days') {
     clampedETDValueStart.value = 30
     clampedETDValueEnd.value = 0
-  } else if(val == 'Past 10 days to next 60 days') {
+  } else if (val == 'Past 10 days to next 60 days') {
     clampedETDValueStart.value = 10
     clampedETDValueEnd.value = 60
   } else {
     clampedETDValueStart.value = 0
     clampedETDValueEnd.value = 0
   }
-  if(clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 0) {
+  if (clampedETDValueStart.value == 0 && clampedETDValueEnd.value == 0) {
     Timestr = ''
   } else {
-    Timestr = 'ETD: minus ' + clampedETDValueStart.value + ' Day(s) to Plus '+ clampedETDValueEnd.value + ' Day(s)'
+    Timestr =
+      'ETD: minus ' +
+      clampedETDValueStart.value +
+      ' Day(s) to Plus ' +
+      clampedETDValueEnd.value +
+      ' Day(s)'
   }
-  emit('ChangeCheckTimeRules', Timestr, clampedETDValueStart.value,clampedETDValueEnd.value)
+  emit('ChangeCheckTimeRules', Timestr, clampedETDValueStart.value, clampedETDValueEnd.value)
 }
 //切换默认值
-const changedefaultradioETA = (val:any) => {
-  if(val == 'Next 30 days') {
+const changedefaultradioETA = (val: any) => {
+  if (val == 'Next 30 days') {
     clampedETAValueStart.value = 0
     clampedETAValueEnd.value = 30
-  } else if(val == 'Next 60 days') {
+  } else if (val == 'Next 60 days') {
     clampedETAValueStart.value = 0
     clampedETAValueEnd.value = 60
-  } else if(val == 'Past 30 days') {
+  } else if (val == 'Past 30 days') {
     clampedETAValueStart.value = 30
     clampedETAValueEnd.value = 0
-  } else if(val == 'Past 10 days to next 60 days') {
+  } else if (val == 'Past 10 days to next 60 days') {
     clampedETAValueStart.value = 10
     clampedETAValueEnd.value = 60
   } else {
     clampedETAValueStart.value = 0
     clampedETAValueEnd.value = 0
   }
-  if(clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 0) {
+  if (clampedETAValueStart.value == 0 && clampedETAValueEnd.value == 0) {
     Timestr = ''
   } else {
-    Timestr = 'ETA: minus ' + clampedETAValueStart.value + ' Day(s) to Plus '+ clampedETAValueEnd.value + ' Day(s)'
+    Timestr =
+      'ETA: minus ' +
+      clampedETAValueStart.value +
+      ' Day(s) to Plus ' +
+      clampedETAValueEnd.value +
+      ' Day(s)'
   }
-  emit('ChangeCheckTimeRules', Timestr, clampedETAValueStart.value,clampedETAValueEnd.value)
+  emit('ChangeCheckTimeRules', Timestr, clampedETAValueStart.value, clampedETAValueEnd.value)
 }
 const handleCloseCreateRule = (val: any) => {
   if (val.indexOf('ETD') != -1 || val.indexOf('ETA') != -1) {
@@ -272,7 +304,10 @@ defineExpose({
     <el-collapse v-model="OceanActive">
       <el-collapse-item name="TransportMode">
         <template #title>
-          <span class="stars_red">*</span><div class="Rules_Title OceanTitle">Transport Mode</div>
+          <div class="Rules_Title_flex">
+            <span class="stars_red">*</span>
+            <div class="Rules_Title OceanTitle">Transport Mode</div>
+          </div>
         </template>
         <div class="oceanCheckbox">
           <el-checkbox-group @change="CheckChange" v-model="TransportCheckedList">
@@ -287,7 +322,10 @@ defineExpose({
       </el-collapse-item>
       <el-collapse-item name="Time">
         <template #title>
-          <span class="stars_red">*</span><div class="Rules_Title OceanTitle">Time</div>
+          <div class="Rules_Title_flex">
+            <span class="stars_red">*</span>
+            <div class="Rules_Title OceanTitle">Time</div>
+          </div>
         </template>
         <div class="oceanCheckbox">
           <el-radio-group v-model="TimeChecked" @change="changeTime">
@@ -297,11 +335,14 @@ defineExpose({
                 <el-radio-group v-model="defaultradio" @change="changedefaultradio">
                   <el-radio-button label="Next 30 days" value="Next 30 days" />
                   <el-radio-button label="Next 60 days" value="Next 60 days" />
-                  <el-radio-button label="Past 10 days to next 60 days" value="Past 10 days to next 60 days" />
+                  <el-radio-button
+                    label="Past 10 days to next 60 days"
+                    value="Past 10 days to next 60 days"
+                  />
                   <el-radio-button label="Past 30 days" value="Past 30 days" />
                   <el-radio-button label="Customize" value="Customize" />
                 </el-radio-group>
-                <div class="flex" style="align-items: end;margin: 0 8px 8px 0;flex-wrap: wrap;">
+                <div class="flex" style="align-items: end; margin: 0 8px 8px 0; flex-wrap: wrap">
                   <div class="date_flex">
                     <div class="time_title">Start Date</div>
                     <div class="flex">
@@ -338,11 +379,14 @@ defineExpose({
                 <el-radio-group v-model="defaultradio2" @change="changedefaultradioETA">
                   <el-radio-button label="Next 30 days" value="Next 30 days" />
                   <el-radio-button label="Next 60 days" value="Next 60 days" />
-                  <el-radio-button label="Past 10 days to next 60 days" value="Past 10 days to next 60 days" />
+                  <el-radio-button
+                    label="Past 10 days to next 60 days"
+                    value="Past 10 days to next 60 days"
+                  />
                   <el-radio-button label="Past 30 days" value="Past 30 days" />
                   <el-radio-button label="Customize" value="Customize" />
                 </el-radio-group>
-                <div class="flex" style="align-items: end;margin: 0 8px 8px 0;flex-wrap: wrap;">
+                <div class="flex" style="align-items: end; margin: 0 8px 8px 0; flex-wrap: wrap">
                   <div class="date_flex">
                     <div class="time_title">Start Date</div>
                     <div class="flex">
@@ -456,7 +500,7 @@ defineExpose({
   :deep(.el-radio-group) {
     display: flex;
     flex-direction: row;
-}
+  }
 }
 :deep(.el-radio) {
   display: flex;
@@ -469,7 +513,7 @@ defineExpose({
   margin-right: 0;
   height: fit-content;
   line-height: 32px;
-  align-items:  start;
+  align-items: start;
 }
 .oceanCheckbox2 {
   :deep(.el-radio-button) {
@@ -487,7 +531,7 @@ defineExpose({
     margin-right: 8px;
   }
 }
-:deep( .el-radio__inner) {
+:deep(.el-radio__inner) {
   border: 1px solid var(--color-system-checkbox-border);
 }
 :deep(.el-radio__input.is-checked + .el-radio__label) {
@@ -500,7 +544,10 @@ defineExpose({
   box-shadow: 0 0 0 1px var(--color-system-input-border) inset;
 }
 :deep(.el-input-group--append .el-input-group__append .el-select .el-select__wrapper) {
-  box-shadow: 0 1px 0 0 var(--color-system-input-border) inset,0 -1px 0 0 var(--color-system-border-1) inset,-1px 0 0 0 var(--color-system-border-1) inset;
+  box-shadow:
+    0 1px 0 0 var(--color-system-input-border) inset,
+    0 -1px 0 0 var(--color-system-border-1) inset,
+    -1px 0 0 0 var(--color-system-border-1) inset;
 }
 :deep(.el-radio-button__inner) {
   border: none;
@@ -516,4 +563,9 @@ defineExpose({
 .date_flex {
   flex: 1;
 }
-</style>
+.Rules_Title_flex {
+  width: 100%;
+  display: flex;
+  align-items: center;
+}
+</style>

+ 7 - 3
src/components/DateRange/src/components/CalendarDate.vue

@@ -29,6 +29,10 @@ const props = defineProps({
   isETA: {
     type: Boolean,
     default: false
+  },
+  isShowPopupClass: {
+    type: Boolean,
+    default: false
   }
 })
 const ETDDate = ref([])
@@ -38,7 +42,6 @@ const CreatePlaceholder = computed(() => {
   } else {
     return ['Start ATA Time', 'End ATA Time']
   }
-
 })
 watch(
   () => props.Date,
@@ -49,7 +52,7 @@ watch(
         current[1] ? dayjs(current[1]).format(valueFormatDate) : ''
       ]
     } else {
-      ETDDate.value  = []
+      ETDDate.value = []
     }
   },
   { immediate: true, deep: true }
@@ -140,10 +143,11 @@ const isTwoDate = (date: any) => {
         width: props.CalendarWidth,
         backgroundColor: props.isType ? 'var(--more-type-bg-color)' : 'var(--management-bg-color)'
       }"
+      :popupClassName="{ 'th-color': props.isShowPopupClass }"
       :open="open"
       :disabled="Disabled"
       @change="changeRangeData"
-      :placeholder="isNeedFooter? ['Start Time', 'End Time'] : CreatePlaceholder"
+      :placeholder="isNeedFooter ? ['Start Time', 'End Time'] : CreatePlaceholder"
       :format="userStore.dateFormat"
       valueFormat="MM/DD/YYYY"
       @openChange="handleCalendarOpen(ETDDate)"

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

@@ -9,6 +9,7 @@ interface UserInfo {
   last_name: string
   user_type: string
   email: string
+  employee_email: string
   expire_day: number
   date_format: string
   numbers_format: string
@@ -86,7 +87,7 @@ export const useUserStore = defineStore('user', {
 
     async logout(isNeedLogout: boolean = true) {
       if (isNeedLogout) {
-        await $api.logout().then(() => {})
+        await $api.logout().then(() => { })
       }
       localStorage.removeItem('userInfo')
       this.userInfo = {}

+ 3 - 0
src/styles/Antdui.scss

@@ -151,6 +151,9 @@ tr
 .ant-picker-dropdown .ant-picker-date-panel .ant-picker-content th {
   color: var(--color-neutral-2);
 }
+.th-color.ant-picker-dropdown .ant-picker-date-panel .ant-picker-content th {
+  color: var(--color-ant-picker-th);
+}
 .ant-picker-dropdown .ant-picker-decade-panel,
 .ant-picker-dropdown .ant-picker-year-panel,
 .ant-picker-dropdown .ant-picker-quarter-panel,

+ 2 - 1
src/styles/elementui.scss

@@ -559,10 +559,11 @@ div .custom-sheader-select .el-select-dropdown__item.is-selected {
 div .el-select-dropdown__item.is-hovering {
   background-color: var(--border-hover-color);
 }
-.el-select-dropdown__item {
+div .el-select-dropdown__item {
   border-radius: var(--border-radius-6);
   margin: 0 8px;
   margin-bottom: 4px;
+  padding:0 32px 0 8px;
   &:last-child {
     margin-bottom: 0;
   }

+ 9 - 0
src/styles/theme.scss

@@ -350,6 +350,10 @@
   --color-guide-icon-bg: rgba(237, 237, 237, 0.6);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.45);
   --color--process-data-tips-bg: #e8ebef;
+
+  --color-ant-picker-th: #b5b9bf;
+
+  --color-json-item-hover: #e6f7ff;
 }
 
 :root.dark {
@@ -573,4 +577,9 @@
   --color-guide-icon-bg: rgba(237, 237, 237, 0.1);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.15);
   --color--process-data-tips-bg: #4f5760;
+  
+  --color-ant-picker-th: rgba(240, 241, 243,0.3);
+
+  --color-json-item-hover: #3e5966;
 }
+  

+ 3 - 0
src/views/AIApiLog/src/components/LogDialog.vue

@@ -87,5 +87,8 @@ defineExpose({
   .el-dialog__body {
     padding: 0;
   }
+  .vjs-tree-node:hover {
+    background-color: var(--color-json-item-hover);
+  }
 }
 </style>

+ 2 - 1
src/views/AIRobotChat/src/components/AIQuestions.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { ref, onMounted } from 'vue'
+import emitter from '@/utils/bus'
 
 const props = defineProps<{
   modalSize: String
@@ -95,7 +96,7 @@ const generatePages = () => {
   pages.value = result.filter((p) => p.row1.length > 0)
 }
 onMounted(() => {
-  AIRobotInit()
+  emitter.on('login-success', AIRobotInit)
 })
 
 defineExpose({

+ 4 - 0
src/views/Booking/src/components/BookingDetail/src/components/EmailView.vue

@@ -137,6 +137,10 @@ const handleFocusEditor = () => {
 const sendEmail = () => {
   const html = editorRef.value.getHtml()
   const text = editorRef.value.getText()
+  if (!text) {
+    ElMessage.warning('Please enter the email content')
+    return
+  }
   $api
     .sendEmailApi({
       action: 'ocean_booking',

+ 1 - 0
src/views/ChatLog/src/components/TableView/src/TableView.vue

@@ -404,6 +404,7 @@ defineExpose({
       <!-- action操作栏的插槽 -->
       <template #action="{ row }">
         <el-button
+          class="el-button--blue"
           style="height: 24px; padding: 8px 4px; padding-left: 5px; font-size: 12px"
           @click="handleLogDetail(row)"
           v-if="row['Question Type'] !== 'Predefined Question'"

+ 5 - 5
src/views/DestinationDelivery/src/DestinationDelivery.vue

@@ -67,12 +67,12 @@ const clickCard = (filterTagItem: string) => {
 }
 
 const DateChange = (date: any) => {
-  queryData.value.created_time_start = date[0]
-  queryData.value.created_time_end = date[1]
+  queryData.value.created_time_start = date ? date[0] : ''
+  queryData.value.created_time_end = date ? date[1] : ''
 }
 const deliveryDataChange = (date) => {
-  queryData.value.delivery_date_start = date[0]
-  queryData.value.delivery_date_end = date[1]
+  queryData.value.delivery_date_start = date ? date[0] : ''
+  queryData.value.delivery_date_end = date ? date[1] : ''
 }
 
 const handleConfigurations = () => {
@@ -138,7 +138,7 @@ const handleSearch = () => {
         </div>
         <div class="date-tips_filter filter-item">
           <span class="label">Creation Date</span>
-          <CalendarDate @DateChange="DateChange"></CalendarDate>
+          <CalendarDate :isShowPopupClass="true" @DateChange="DateChange"></CalendarDate>
         </div>
         <div class="input-tips_filter filter-item">
           <el-input

+ 28 - 34
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue

@@ -30,7 +30,7 @@ const CancelRulesVisible = ref(false)
 const SaveedVisible = ref(false)
 const UnableSaveVisible = ref(false)
 const missingmessage = ref('')
-const KLNPLCvalue = ref('')
+const KLNPLCvalue = ref([])
 interface KLNItem {
   value: string
   label: string
@@ -50,7 +50,7 @@ const InitRuleData = () => {
     .then((res: any) => {
       if (res.code === 200) {
         const { returnData } = res.data
-        KLNPLCvalue.value = returnData.kln_pic
+        KLNPLCvalue.value = returnData.KLNPLCvalue
         selectedCountry.value = returnData.country
         setbookingdata.value = returnData.SetBookingWindow
         windowRadio.value = returnData.SetBookingWindow.windowradio
@@ -80,8 +80,7 @@ const CreateRuleDisabled = computed(() => {
     selectedCountry.value === '' ||
     windowRadio.value === undefined ||
     recommendRadio.value === undefined ||
-    KLNPLCvalue.value === '' || 
-    KLNPLCvalue.value === '无搜索结果'
+    KLNPLCvalue.value.length === 0
   ) {
     return true;
   }
@@ -127,16 +126,14 @@ const CreateRuleDisabled = computed(() => {
   // 4. 所有条件都满足,返回 false(不禁用)
   return false;
 });
-// select country
+// select country 
 const handleClickSelectCountry = (val:any) =>{
   selectedCountry.value = val
   countryCheckedList.value = []
-  getKLNList()
 }
 // select station list
 const handleChangeStation = (val:any) =>{
   countryCheckedList.value = val
-  getKLNList()
 }
 
 // select booking window
@@ -159,23 +156,6 @@ const checkRecommend = (checked: any, airlist: any, sealist: any, radio: number)
   recommendRadio.value = radio
 }
 
-// 获取KLN列表
-const getKLNList = (): Promise<KLNItem[]> => {
-  return new Promise((resolve) => {
-    $api.getKLNEmployeeList({ 
-      term: '',
-      station: countryCheckedList.value
-    })
-      .then((res: any) => {
-        if (res.code === 200) {
-          KLNLists.value = res.data
-        } else {
-          resolve([]); // 失败返回空数组
-        }
-      })
-      .catch(() => resolve([])); // 异常兜底
-  });
-};
 // 自动查询KLN
 const loading = ref(false)
 const options = ref<KLNItem[]>([])
@@ -183,10 +163,19 @@ const querySearchAsync = (query: string) => {
   if (query) {
     loading.value = true
     setTimeout(() => {
-      loading.value = false
-      options.value = KLNLists.value.filter((item) => {
-        return item.label.toLowerCase().includes(query.toLowerCase())
+      $api.getKLNEmployeeList({ 
+        term: query,
+        station: countryCheckedList.value
       })
+        .then((res: any) => {
+          if (res.code === 200) {
+            loading.value = false
+            KLNLists.value = res.data
+            options.value = res.data.filter((item) => {
+              return item.label.toLowerCase().includes(query.toLowerCase())
+            })
+          }
+        })
     }, 1000)
   } else {
     options.value = []
@@ -245,12 +234,14 @@ const handleSubmitRule = () => {
       mergeData = [...mergeData , ...seaList]
     }
   }
-  airData = Object.keys(seaList?.[0])
-  airData.forEach((item) => {
-    Object.assign(airlistInfo, {
-      [item]: mergeData.map((row) => row[item])
+  if(seaList.length != 0) {
+    airData = Object.keys(seaList?.[0])
+    airData.forEach((item) => {
+      Object.assign(airlistInfo, {
+        [item]: mergeData.map((row) => row[item])
+      })
     })
-  })
+  }
   $api.handelSaveRule({
     serial_no: a != undefined ? a: '',
     country: selectedCountry.value,
@@ -303,7 +294,7 @@ onMounted(() => {
             "
             class="font_family icon-icon_submit_b"
           ></span>
-          <span style="font-weight: 400">Submit</span>
+          <span style="font-weight: 400">Save</span>
         </el-button>
         <!-- 取消保存 -->
         <el-dialog v-model="CancelRulesVisible" width="480">
@@ -445,7 +436,7 @@ onMounted(() => {
                     ></use>
                   </svg>
                 </span>
-                <span class="stars_red">*</span>KLN PLC
+                <span class="stars_red">*</span>KLN PIC
               </div>
             </template>
             <div>
@@ -453,6 +444,7 @@ onMounted(() => {
                 v-model="KLNPLCvalue"
                 filterable
                 remote
+                multiple
                 placeholder="Select Employee Account"
                 :remote-method="querySearchAsync"
                 :loading="loading"
@@ -506,6 +498,8 @@ onMounted(() => {
 .setting-top-title {
   background-color: var(--color-shipment-status-header-bg);
   height: 48px;
+  font-size: 18px;
+  font-weight: bold;
   padding: 0 16px;
   display: flex;
   align-items: center;

+ 17 - 90
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/RecommendDate.vue

@@ -85,19 +85,19 @@ const recommendata = ref(props.recommendata)
 const initRecommendData = () => {
   if(recommendata.value) {
     Recommendradio.value = recommendata.value.Recommendradio
-  if(Recommendradio.value == 2) {
-    isRecommendETA.value = true
-    RecommendCheckedList.value = recommendata.value.RecommendCheckedList
-    if(RecommendCheckedList.value.includes('Air')) {
-      isAir.value = true
+    if(Recommendradio.value == 2) {
+      isRecommendETA.value = true
+      RecommendCheckedList.value = recommendata.value.RecommendCheckedList
+      if(RecommendCheckedList.value.includes('Air')) {
+        isAir.value = true
+      }
+      if(RecommendCheckedList.value.includes('Sea')) {
+        isSea.value = true
+      }
+      AirContentList.value = recommendata.value.RecommendCheckedAirList
+      SeaContentList.value = recommendata.value.RecommendCheckedSeaList
     }
-    if(RecommendCheckedList.value.includes('Sea')) {
-      isSea.value = true
     }
-    AirContentList.value = recommendata.value.RecommendCheckedAirList
-    SeaContentList.value = recommendata.value.RecommendCheckedSeaList
-  } 
-  }
 }
 
 watch(() => props.recommendata, (val) => { 
@@ -119,7 +119,7 @@ function createRuleItem(type: 'Air' | 'Sea', ruleType: string): RuleItem {
       ...baseItem,
       ports: ruleType === '*Default Rule' ? ['ALL'] : [],
       mode_type: 'air',
-      PortList: JSON.parse(JSON.stringify(AirPorList.value))
+      // PortList: JSON.parse(JSON.stringify(AirPorList.value))
     }
   }
   return {
@@ -127,8 +127,8 @@ function createRuleItem(type: 'Air' | 'Sea', ruleType: string): RuleItem {
     ports: ruleType === '*Default Rule' ? ['ALL'] : [],
     carrier: ruleType === '*Default Rule' ? ['ALL'] : [],
     mode_type: 'sea',
-    PortList: JSON.parse(JSON.stringify(SeaPortList.value)),
-    CarrierList: JSON.parse(JSON.stringify(SeaCarrierList.value))
+    // PortList: JSON.parse(JSON.stringify(SeaPortList.value)),
+    // CarrierList: JSON.parse(JSON.stringify(SeaCarrierList.value))
   }
 }
 
@@ -375,76 +375,6 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
   item.rule_type = val
   updatePriorities()
 }
-
-// 获取Air Port/Port/Carrier的值
-const AirPorList = ref([])
-const SeaPortList = ref([])
-const SeaCarrierList = ref([])
-const getPortList = (type: any) => {
-  $api.getPortList({ 
-    term: '',
-    mode: type
-  }).then((res: any) => {
-    if (res.code === 200) {
-      if(type === 'air') {
-        AirPorList.value = res.data
-        // 更新现有行的列表
-        AirContentList.value.forEach(item => {
-          item.PortList = JSON.parse(JSON.stringify(res.data))
-          setTimeout(() => {
-            if(AirPortRef.value) {
-              AirPortRef.value.forEach(item1 => { 
-                item1.initGetPortsList(item.PortList)
-              })
-            }
-          }, 1000);
-        })
-      }
-      if(type === 'sea') {
-        SeaPortList.value = res.data
-        // 更新现有行的列表
-        SeaContentList.value.forEach(item => {
-          item.PortList = JSON.parse(JSON.stringify(res.data))
-          setTimeout(() => {
-            if(SeaPortRef.value) {
-              SeaPortRef.value.forEach(item1 => { 
-                item1.initGetPortsList(item.PortList)
-              })
-            }
-          }, 1000);
-        })
-      }
-    }
-  })
-}
-
-// 获取Carrier列表
-const getCarrierList = () => {
-  $api.getCarrierList({ 
-    term: '',
-  }).then((res: any) => {
-    if (res.code === 200) {
-      SeaCarrierList.value = res.data
-      // 更新现有行的列表
-      SeaContentList.value.forEach(item => {
-        item.CarrierList = JSON.parse(JSON.stringify(res.data))
-        setTimeout(() => {
-          if(SeaCarrierRef.value) {
-            SeaCarrierRef.value.forEach(item1 => { 
-              item1.initGetPortsList(item.CarrierList)
-            })
-          }
-        }, 1000);
-      })
-    }
-  })
-}
-
-onMounted(() => {
-  getPortList('air')
-  getPortList('sea')
-  getCarrierList()
-})
 </script>
 
 <template>
@@ -498,8 +428,7 @@ onMounted(() => {
                       :SelectIndex="index"
                       :SelectedValue="item.ports"
                       :typeisDisabled="item.rule_type"
-                      :PortList="item.PortList"
-                      SelectType="Air"
+                      SelectType="air"
                       @changeSelectedValue="val => changeSelectedValue(val, index, 'ports', AirContentList)"
                     />
                   </div>
@@ -574,8 +503,7 @@ onMounted(() => {
                       :SelectIndex="index"
                       :SelectedValue="item.ports"
                       :typeisDisabled="item.rule_type"
-                      :PortList="item.PortList"
-                      SelectType="Sea"
+                      SelectType="sea"
                       @changeSelectedValue="val => changeSelectedValue(val, index, 'ports', SeaContentList)"
                     />
                   </div>
@@ -585,8 +513,7 @@ onMounted(() => {
                       :SelectIndex="index"
                       :SelectedValue="item.carrier"
                       :typeisDisabled="item.rule_type"
-                      :PortList="item.CarrierList"
-                      SelectType="Sea"
+                      SelectType="carrier"
                       @changeSelectedValue="val => changeSelectedValue(val, index, 'carrier', SeaContentList)"
                     />
                   </div>

+ 1 - 1
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectStation.vue

@@ -71,7 +71,7 @@ const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
     if(results.length == 0) {
       cb([{ 
         isNoData: true, 
-        value: '无搜索结果' 
+        value: 'No Data' 
       }]);
     } else {
       cb(results)

+ 61 - 9
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue

@@ -30,6 +30,7 @@ const props = defineProps({
   }
 })
 const checkAll = ref(false)
+const loading = ref(false)
 const value = ref<string[]>(props.SelectedValue)
 const SelectNumber = ref(props.SelectIndex)
 const typeisDisabled = ref(props.typeisDisabled)
@@ -52,12 +53,63 @@ watch(
   }
 , { immediate: true, deep: true })
 
-const initGetPortsList = (val: any) => {
-  PortList.value = val.map(item => ({
-    ...item,
-    checked: value.value.includes(item.value)
-  }));
+const list = ref<PortOption[]>([])
+const remoteMethod = (query: string) => {
+  if (query) {
+    loading.value = true
+    setTimeout(() => {
+      if(props.SelectType == 'carrier') {
+        $api
+        .getCarrierList({
+          term: query
+        })
+        .then((res: any) => {
+          loading.value = false
+          if (res.code == 200) {
+            if(value.value[0] == 'ALL') {
+              list.value = res.data.map((item: any) => {
+                return { value: item.value, label: item.label, checked: true }
+              })
+            } else {
+              list.value = res.data.map((item: any) => {
+                return { value: item.value, label: item.label, checked: value.value?.includes(item.value) }
+              })
+            }
+            PortList.value = list.value.filter((item) => {
+              return item.label.toLowerCase().includes(query.toLowerCase())
+            })
+          }
+        })
+      } else {
+        $api
+        .getPortList({
+          term: query,
+          mode: props.SelectType
+        })
+        .then((res: any) => {
+          loading.value = false
+          if (res.code == 200) {
+            if(value.value[0] == 'ALL') {
+              list.value = res.data.map((item: any) => {
+                return { value: item.value, label: item.label, checked: true }
+              })
+            } else {
+              list.value = res.data.map((item: any) => {
+                return { value: item.value, label: item.label, checked: value.value?.includes(item.value) }
+              })
+            }
+            PortList.value = list.value.filter((item) => {
+              return item.label.toLowerCase().includes(query.toLowerCase())
+            })
+          }
+        })
+      }
+    }, 200)
+  } else {
+    PortList.value = []
+  }
 }
+
 const emits = defineEmits(['changeSelectedValue', 'handelremovetag'])
 
 watch(value, (val) => {
@@ -113,7 +165,7 @@ const handelRemoveTag = (tag) => {
 }
 
 const displayNumber = computed(() => {
-  return props.SelectType === 'Air' ? 6 : 3
+  return props.SelectType === 'air' ? 6 : 3
 })
 
 const visibleTags = computed(() => { 
@@ -123,9 +175,6 @@ const hiddenTagsCount = computed(() => {
   return Math.max(value.value.length - displayNumber.value, 0);
 })
 
-defineExpose({
-  initGetPortsList
-})
 </script>
 <template>
   <div style="width: 100%;">
@@ -133,10 +182,13 @@ defineExpose({
       v-model="value"
       multiple
       filterable
+      remote
       :disabled="typeisDisabled  == '*Default Rule'"
       placeholder="Select"
+      :remote-method="remoteMethod"
       popper-class="custom-sheader-select"
       @change="handelchangeSelect"
+      :loading="loading"
     >
       <template #header>
         <a-checkbox v-model:checked="checkAll" @change="handleCheckAll">ALL</a-checkbox>

File diff suppressed because it is too large
+ 404 - 252
src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue


+ 2 - 1
src/views/DestinationDelivery/src/components/CreateNewBooking/src/components/NewbookingTable.vue

@@ -21,7 +21,8 @@ const tableData = ref<VxeGridProps<any>>({
   maxHeight: 288,
   columns: [],
   data: [],
-  scrollY: { enabled: true, oSize: 20, gt: 30 },
+  scrollY: { enabled: true, oSize: 20, gt: 20, scrollToTopOnChange: true },
+  scrollX: { enabled: true, gt: 10, scrollToLeftOnChange: true },
   emptyText: ' ',
   showHeaderOverflow: true,
   showOverflow: true,

+ 42 - 5
src/views/DestinationDelivery/src/components/DeliveryDate.vue

@@ -151,13 +151,13 @@ const handlePanelChange = (value: any, mode: any) => {
             <div class="status-item pending">
               <span class="status-text">Pending</span>
               <div class="count">
-                <span>3</span>
+                <span>{{ stateDataList[current.format('YYYY-MM-DD')].pending }}</span>
               </div>
             </div>
             <div class="status-item approved">
               <span class="status-text">Approved</span>
               <div class="count">
-                <span>3</span>
+                <span>{{ stateDataList[current.format('YYYY-MM-DD')].approved }}</span>
               </div>
             </div>
           </div>
@@ -343,6 +343,16 @@ div.delivery-date-range-picker {
     display: none !important;
   }
 
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-start:not(
+      .ant-picker-cell-range-start-single
+    ):before,
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-end:not(
+      .ant-picker-cell-range-end-single
+    ):before {
+    background-color: var(--color-theme) !important;
+  }
   &:where(.css-dev-only-do-not-override-1p3hq3p).ant-picker-dropdown
     .ant-picker-cell-in-view.ant-picker-cell-in-range::before {
     background-color: transparent !important;
@@ -358,6 +368,36 @@ div.delivery-date-range-picker {
     ) {
     background-color: transparent !important;
   }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-end.ant-picker-cell-selected.ant-picker-cell-range-end:not(
+      .ant-picker-cell-range-end-single
+    ):before {
+    background-color: var(--color-theme) !important;
+  }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cellrange-end:not(
+      .ant-picker-cell-range-end-single
+    ).ant-picker-cell-range-end.ant-picker-cell-range-hover-end:before {
+    background-color: var(--color-theme) !important;
+  }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cellrange-start:not(
+      .ant-picker-cell-range-start-single
+    ).ant-picker-cell-range-start.ant-picker-cell-range-hover-start:before {
+    background-color: var(--color-theme) !important;
+  }
+  &.ant-picker-dropdown
+    .ant-picker-cell-in-view.ant-picker-cell-range-start.ant-picker-cell-range-start:not(
+      .ant-picker-cell-range-start-single
+    ):before {
+    background-color: var(--color-theme) !important;
+  }
+  &:where(.css-1p3hq3p).ant-picker-dropdown .ant-picker-cell-disabled {
+    background-color: rgba(0, 0, 0, 0.04) !important;
+    &::before {
+      background-color: transparent !important;
+    }
+  }
   .date-cell {
     position: relative;
     height: 100%;
@@ -479,6 +519,3 @@ div.delivery-date-range-picker {
   }
 }
 </style>
-ant-picker-cell ant-picker-cell-in-view ant-picker-cell-range-end ant-picker-cell-range-end-single
-ant-picker-cell-selected
-<style lang="scss"></style>

+ 28 - 5
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -9,6 +9,9 @@ import BookingDetailDialog from './components/BookingDetailDialog.vue'
 import EmailDialog from './components/EmailDialog.vue'
 import TipsDialog from './components/TipsDialog.vue'
 import { useRouter } from 'vue-router'
+import { useUserStore } from '@/stores/modules/user'
+
+const userStore = useUserStore()
 const router = useRouter()
 
 const props = defineProps({
@@ -388,7 +391,11 @@ defineExpose({
         </el-button>
         <!-- email -->
         <el-button
-          v-if="!isEmployeeRole && row.status === 'Approved'"
+          v-if="
+            !isEmployeeRole &&
+            row.status === 'Approved' &&
+            row.modify_by === userStore.userInfo.uname
+          "
           @click="clickEmailBtn(row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
@@ -400,7 +407,11 @@ defineExpose({
           @click="handleEdit(row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
-          v-if="!isEmployeeRole && (row.status === 'Pending Approval' || row.status === 'Rejected')"
+          v-if="
+            !isEmployeeRole &&
+            (row.status === 'Pending Approval' || row.status === 'Rejected') &&
+            row.modify_by === userStore.userInfo.uname
+          "
         >
           <span class="font_family icon-icon_edit_b"> </span>
         </el-button>
@@ -409,7 +420,11 @@ defineExpose({
           @click="handleTips('cancel', row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
-          v-if="!isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="
+            !isEmployeeRole &&
+            row.status === 'Pending Approval' &&
+            row.modify_by === userStore.userInfo.uname
+          "
         >
           <span class="font_family icon-icon_cancelled_b"> </span>
         </el-button>
@@ -417,14 +432,22 @@ defineExpose({
         <el-button
           @click="handleTips('approve', row)"
           class="action-btn el-button--blue"
-          v-if="isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="
+            isEmployeeRole &&
+            row.status === 'Pending Approval' &&
+            row.kln_pic.includes(userStore.userInfo.employee_email)
+          "
           style="height: 24px; width: 24px"
         >
           <span class="font_family icon-icon_confirm_b"> </span>
         </el-button>
         <!-- reject -->
         <el-button
-          v-if="isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="
+            isEmployeeRole &&
+            row.status === 'Pending Approval' &&
+            row.kln_pic.includes(userStore.userInfo.employee_email)
+          "
           @click="handleTips('reject', row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"

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

@@ -151,6 +151,10 @@ const handleFocusEditor = () => {
 const sendEmail = () => {
   const html = editorRef.value.getHtml()
   const text = editorRef.value.getText()
+  if (!text) {
+    ElMessage.warning('Please enter the email content')
+    return
+  }
   $api
     .saveDliveryBookingEmail({
       email: emailData.value.email,

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

@@ -18,82 +18,82 @@ watch(
   }
 )
 const getMenuList = () => {
-  // $api.getMenuList().then((res) => {
-  //   if (res.code === 200) {
-  //     menuList.value = res.data
-  //   }
-  // })
-  menuList.value = [
-    {
-      index: '1',
-      label: 'Dashboard',
-      icon: 'icon_data_fill_b',
-      path: '/dashboard'
-    },
-    {
-      index: '2',
-      label: 'Booking',
-      icon: 'icon_booking__fill_b',
-      // path: '/booking',
-      type: 'list',
-      children: [
-        {
-          index: '2-1',
-          label: 'Booking Management',
-          path: '/booking'
-        },
-        {
-          index: '2-2',
-          label: 'Destination Delivery',
-          path: '/destination-delivery'
-        }
-      ]
-    },
-    {
-      index: '3',
-      label: 'Tracking',
-      icon: 'icon_tracking__fill_b',
-      path: '/tracking'
-    },
-    {
-      index: '4',
-      label: 'System Management',
-      icon: 'icon_system__management_fill_b',
-      type: 'list',
-      children: [
-        {
-          index: '4-1',
-          label: 'System Message',
-          path: '/system-message'
-        },
-        {
-          index: '4-2',
-          label: 'System Settings',
-          path: '/SystemSettings'
-        },
-        {
-          index: '4-3',
-          label: 'Chat Log',
-          path: '/chat-log'
-        },
-        {
-          index: '4-4',
-          label: 'AI API Log',
-          path: '/ai-api-log'
-        },
-        {
-          index: '4-5',
-          label: 'Operation Log',
-          path: '/Operationlog'
-        },
-        {
-          index: '4-6',
-          label: 'Prompt Configuration',
-          path: '/PromptConfiguration'
-        }
-      ]
+  $api.getMenuList().then((res) => {
+    if (res.code === 200) {
+      menuList.value = res.data
     }
-  ]
+  })
+  // menuList.value = [
+  //   {
+  //     index: '1',
+  //     label: 'Dashboard',
+  //     icon: 'icon_data_fill_b',
+  //     path: '/dashboard'
+  //   },
+  //   {
+  //     index: '2',
+  //     label: 'Booking',
+  //     icon: 'icon_booking__fill_b',
+  //     // path: '/booking',
+  //     type: 'list',
+  //     children: [
+  //       {
+  //         index: '2-1',
+  //         label: 'Booking Management',
+  //         path: '/booking'
+  //       },
+  //       {
+  //         index: '2-2',
+  //         label: 'Destination Delivery',
+  //         path: '/destination-delivery'
+  //       }
+  //     ]
+  //   },
+  //   {
+  //     index: '3',
+  //     label: 'Tracking',
+  //     icon: 'icon_tracking__fill_b',
+  //     path: '/tracking'
+  //   },
+  //   {
+  //     index: '4',
+  //     label: 'System Management',
+  //     icon: 'icon_system__management_fill_b',
+  //     type: 'list',
+  //     children: [
+  //       {
+  //         index: '4-1',
+  //         label: 'System Message',
+  //         path: '/system-message'
+  //       },
+  //       {
+  //         index: '4-2',
+  //         label: 'System Settings',
+  //         path: '/SystemSettings'
+  //       },
+  //       {
+  //         index: '4-3',
+  //         label: 'Chat Log',
+  //         path: '/chat-log'
+  //       },
+  //       {
+  //         index: '4-4',
+  //         label: 'AI API Log',
+  //         path: '/ai-api-log'
+  //       },
+  //       {
+  //         index: '4-5',
+  //         label: 'Operation Log',
+  //         path: '/Operationlog'
+  //       },
+  //       {
+  //         index: '4-6',
+  //         label: 'Prompt Configuration',
+  //         path: '/PromptConfiguration'
+  //       }
+  //     ]
+  //   }
+  // ]
 }
 getMenuList()
 //监听窗口大小

+ 97 - 0
src/views/Login/src/components/ActivatedDialog.vue

@@ -0,0 +1,97 @@
+<script setup lang="ts">
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+const dialogModel = ref(false)
+const state = ref<'reset' | 'activate'>('activate')
+
+const openDialog = (stateValue: 'reset' | 'activate') => {
+  state.value = stateValue
+  dialogModel.value = true
+}
+
+const tipsData = ref({
+  reset: {
+    title: 'Password Reset Successfully',
+    firstLine: 'Your password has been successfully reset.',
+    secondLine: 'You can now log in using your email and your new password.'
+  },
+  activate: {
+    title: 'Account Activated Successfully',
+    firstLine: 'Your account has been successfully activated.',
+    secondLine: 'You can now log in using your email and the password you just created.'
+  }
+})
+const handleGotoLogin = () => {
+  dialogModel.value = false
+  router.push({ name: 'Login' })
+}
+
+defineExpose({
+  openDialog
+})
+</script>
+
+<template>
+  <el-dialog v-model="dialogModel" top="23vh" class="activated-dialog" width="480">
+    <div class="activated-dialog-content">
+      <div class="successfully-icon">
+        <span style="font-size: 60px; color: white" class="font_family icon-icon_confirm_b"></span>
+      </div>
+      <div class="title">{{ tipsData[state].title }}</div>
+      <div class="description">
+        <p>{{ tipsData[state].firstLine }}</p>
+        <p>{{ tipsData[state].secondLine }}</p>
+      </div>
+    </div>
+    <template #footer>
+      <el-button style="width: 120px; height: 40px" class="el-button--dark" @click="handleGotoLogin"
+        >Go to Login</el-button
+      >
+    </template>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.activated-dialog {
+  background-color: red;
+  .successfully-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100px;
+    height: 100px;
+    background-color: rgb(0, 168, 112);
+    border-radius: 50%;
+  }
+  .activated-dialog-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding-top: 16px;
+  }
+  .title {
+    margin-top: 12px;
+    font-size: 18px;
+    font-weight: 700;
+  }
+  .description {
+    margin-top: 10px;
+    margin-bottom: 8px;
+    text-align: center;
+    line-height: 21px;
+  }
+}
+</style>
+<style lang="scss">
+.activated-dialog {
+  .el-dialog__header {
+    display: none;
+  }
+  .el-dialog__footer {
+    padding-left: 16px;
+    text-align: center;
+  }
+}
+</style>

+ 328 - 0
src/views/Login/src/components/SetPasswordDialog.vue

@@ -0,0 +1,328 @@
+<script setup lang="ts">
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+const dialogModel = ref(false)
+const state = ref<'reset' | 'activate'>('activate')
+
+const openDialog = (stateValue: 'reset' | 'activate') => {
+  state.value = stateValue
+  dialogModel.value = true
+}
+
+const loginForm = ref({
+  username: '',
+  newPassword: '',
+  confirmPassword: ''
+})
+
+const newPwdErrTips = ref('')
+const loginError: any = ref({
+  newPassword: false,
+  confirmPassword: false
+})
+
+const emit = defineEmits(['submit'])
+
+const handleChangePwd = () => {
+  if (loginForm.value.newPassword !== loginForm.value.confirmPassword) {
+    loginError.value.confirmPassword = true
+    return
+  }
+  if (loginForm.value.newPassword.length < 12 || loginForm.value.newPassword.length > 20) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password length between 12 - 20'
+    return
+  }
+  if (!/[A-Z]/.test(loginForm.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain uppercase letters'
+    return
+  }
+  if (!/[a-z]/.test(loginForm.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain lowercase letters'
+    return
+  }
+  if (!/[0-9]/.test(loginForm.value.newPassword)) {
+    loginError.value.newPassword = true
+    newPwdErrTips.value = 'Password must contain numbers'
+    return
+  }
+  $api
+    .resetAndActivatePassword({
+      verifcation_code: route.query.verifcation_code,
+      password: loginForm.value.newPassword
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        emit('submit')
+        dialogModel.value = false
+      } else if (res.code !== 500 && res.code !== 200 && res.code) {
+        ElMessage.error('Error! Please try again later.')
+      }
+    })
+}
+
+const hiddenError = (key: string) => {
+  loginError.value[key] = false
+}
+
+const hasUppercase = ref(false)
+const hasLowercase = ref(false)
+const hasNumber = ref(false)
+const isValidLength = ref(false)
+const checkPassword = () => {
+  const pwd = loginForm.value.newPassword
+  // 检测是否包含大写字母
+  hasUppercase.value = /[A-Z]/.test(pwd)
+  // 检测是否包含小写字母
+  hasLowercase.value = /[a-z]/.test(pwd)
+  // 检测是否包含数字
+  hasNumber.value = /[0-9]/.test(pwd)
+  // 检测长度是否符合要求
+  isValidLength.value = pwd.length >= 12 && pwd.length <= 20
+}
+
+const confirmPwd = () => {
+  if (loginForm.value.confirmPassword !== loginForm.value.newPassword) {
+    loginError.value.confirmPassword = true
+  } else {
+    loginError.value.confirmPassword = false
+  }
+}
+defineExpose({
+  openDialog
+})
+</script>
+
+<template>
+  <el-dialog v-model="dialogModel" class="set-password-dialog" width="480">
+    <div class="content">
+      <div class="top-section">
+        <div class="title">Set Your Password</div>
+        <div class="description">
+          Hello {{ route.query.name }}, please create a new password for your account.
+        </div>
+      </div>
+      <div class="login-form">
+        <div class="label" style="margin-top: 0">
+          <span>New Password</span>
+        </div>
+        <el-input
+          ref="passWordRef"
+          :class="{ 'is-error': loginError.newPassword }"
+          v-model="loginForm.newPassword"
+          type="password"
+          placeholder="Please input password"
+          show-password
+          @focus="hiddenError('newPassword')"
+          @input="checkPassword"
+        >
+          <template #prefix>
+            <span class="font_family icon-icon_password_b"></span>
+          </template>
+        </el-input>
+        <div class="error" v-if="loginError.newPassword">{{ newPwdErrTips }}</div>
+        <div class="limit-tips">
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasUppercase }"></span>
+            <span>Password must contain uppercase letters</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasLowercase }"></span>
+            <span>Password must contain lowercase letters</span>
+          </div>
+          <div class="tip-item">
+            <span class="font_family icon-icon_confirm_b" :class="{ active: hasNumber }"></span>
+            <span>Password must contain numbers</span>
+          </div>
+          <div class="tip-item">
+            <span
+              style="padding-top: 2px"
+              class="font_family icon-icon_confirm_b"
+              :class="{ active: isValidLength }"
+            ></span>
+            <span>Password length between12 - 20 </span>
+          </div>
+        </div>
+        <div class="label">
+          <span>Confirm New Password</span>
+        </div>
+
+        <el-input
+          ref="passWordRef"
+          :class="{ 'is-error': loginError.confirmPassword }"
+          v-model="loginForm.confirmPassword"
+          type="password"
+          placeholder="Please input password"
+          show-password
+          @focus="hiddenError('confirmPassword')"
+          @blur="confirmPwd"
+        >
+          <template #prefix>
+            <span class="font_family icon-icon_password_b"></span>
+          </template>
+        </el-input>
+        <div class="error" v-if="loginError.confirmPassword">
+          The password does not match. Please try again.
+        </div>
+        <el-button @click="handleChangePwd" class="el-button--dark set-btn">{{
+          state === 'reset' ? 'Reset Password' : 'Set Password & Activate Account'
+        }}</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.set-password-dialog {
+  .content {
+    padding-bottom: 40px;
+    .top-section {
+      width: 100%;
+      height: 158px;
+      padding-top: 64px;
+      background: url(../image/bg-login-card.png) no-repeat;
+      background-position: top left; /* 从左上角开始显示 */
+      background-size: 480px 200px; /* 保持背景图像的原始尺寸 */
+      .title {
+        font-size: 24px;
+        font-weight: 700;
+        text-align: center;
+      }
+      .description {
+        margin-top: 16px;
+        text-align: center;
+      }
+    }
+    .set-btn {
+      width: 100%;
+      height: 40px;
+      margin-top: 16px;
+    }
+  }
+  .login-form {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    padding: 0 40px;
+    .el-input {
+      height: 40px;
+      .confirm-icon {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        height: 16px;
+        width: 16px;
+        margin-right: 4px;
+        font-size: 14px;
+        color: #fff;
+        background-color: var(--color-success);
+        border-radius: 50%;
+      }
+
+      &.is-error {
+        :deep(.el-input__wrapper) {
+          box-shadow: 0 0 0 1px var(--color-danger) inset;
+        }
+      }
+
+      :deep(.el-input__prefix) {
+        margin: 0 4px;
+        background-color: transparent;
+      }
+      &.is-disabled {
+        :deep(.el-input__inner) {
+          -webkit-text-fill-color: var(--color-neutral-1);
+          color: var(--color-neutral-1);
+          font-weight: 700;
+        }
+      }
+    }
+    .limit-tips {
+      margin-top: 10px;
+      .tip-item {
+        display: flex;
+        align-items: center;
+        margin-bottom: 8px;
+        font-size: 12px;
+        color: var(--color-neutral-2);
+        &:nth-last-child(1) {
+          margin-bottom: 0;
+        }
+        .font_family {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          height: 12px;
+          width: 12px;
+          padding-top: 1px;
+          background-color: #b5b9bf;
+          font-size: 10px;
+          color: white;
+          border-radius: 50%;
+          &.active {
+            background-color: #00a870;
+          }
+        }
+        span {
+          margin-left: 4px;
+        }
+      }
+    }
+    .verification-code {
+      margin-top: 16px;
+      .verification-code-img {
+        width: 130px;
+        height: 38px;
+        object-fit: cover;
+      }
+      :deep(.el-input-group__append) {
+        padding: 0;
+        padding-right: 1px;
+      }
+    }
+    .el-input.user-name {
+      :deep(.el-input__wrapper) {
+        padding-right: 6px;
+        box-shadow: 0 0 0 1px var(--color-input-disabled-border) inset;
+      }
+    }
+
+    .label {
+      display: flex;
+      justify-content: space-between;
+      width: 100%;
+      margin-top: 14px;
+      font-size: 12px;
+      line-height: 18px;
+
+      span {
+        color: var(--color-neutral-2);
+      }
+
+      .forgot-password {
+        color: var(--color-theme);
+        cursor: pointer;
+      }
+    }
+
+    .error {
+      font-size: 12px;
+      color: var(--color-danger);
+      line-height: 14px;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.set-password-dialog {
+  .el-dialog__header {
+    display: none;
+  }
+  .el-dialog__body {
+    padding: 0;
+  }
+}
+</style>

+ 19 - 2
src/views/Login/src/loginView.vue

@@ -7,11 +7,26 @@ import { useThemeStore } from '@/stores/modules/theme'
 import ScoringSystem from '@/views/Dashboard/src/components/ScoringSystem.vue'
 import CryptoJS from 'crypto-js'
 import dayjs from 'dayjs'
+import ActivatedDialog from './components/ActivatedDialog.vue'
+import SetPasswordDialog from './components/SetPasswordDialog.vue'
 
 const router = useRouter()
 const route = useRoute()
 const themeStore = useThemeStore()
 
+const activatedDialogState = ref()
+const activatedDialogRef = ref()
+const setPasswordDialogRef = ref()
+onMounted(() => {
+  if (route.query.state) {
+    activatedDialogState.value = route.query.state
+    setPasswordDialogRef.value.openDialog(activatedDialogState.value)
+  }
+})
+const handleSetPassword = () => {
+  activatedDialogRef.value.openDialog(activatedDialogState.value)
+}
+
 // 手动获取url中的参数,直接获取route.query的参数时如果有+号会被转义成空格
 const getQueryParams = (url: string) => {
   const params = url.split('?')[1]
@@ -415,7 +430,7 @@ const firstLoginTipsRef = ref()
     </el-card>
     <el-card class="login-card" v-else-if="status === 'reset'">
       <div class="card-title" :class="{ 'is-dark': themeStore.theme === 'dark' }">
-        <span class="welcome">Password Retrieval</span>
+        <span class="welcome">Reset Password</span>
         <span class="tips">We'll send your password to your email address.</span>
       </div>
       <div class="login-form">
@@ -455,7 +470,7 @@ const firstLoginTipsRef = ref()
         <div class="error" v-if="loginError.email">Incorrect email. Please try again.</div>
 
         <el-button @click="handleVerification" class="el-button--dark login-btn"
-          >Send Password</el-button
+          >Send Reset Link</el-button
         >
         <div @click="backLogin(false)" class="back-text">
           <span class="font_family icon-icon_back_b"></span>
@@ -476,6 +491,8 @@ const firstLoginTipsRef = ref()
     ></VSliderVerification>
     <ErrorTips ref="errorTipsRef" @forget-password="status = 'reset'"></ErrorTips>
     <FirstLoginTips ref="firstLoginTipsRef" @forget-password="status = 'reset'"></FirstLoginTips>
+    <ActivatedDialog ref="activatedDialogRef"></ActivatedDialog>
+    <SetPasswordDialog @submit="handleSetPassword" ref="setPasswordDialogRef"></SetPasswordDialog>
   </div>
 </template>
 

+ 8 - 11
src/views/SystemSettings/src/components/MonitoringTable/src/MonitoringTable.vue

@@ -45,18 +45,12 @@ const datatest = ref([])
 const tableData = ref<VxeGridProps<any>>({
   border: true,
   round: true,
-  minHeight: 660,
   maxHeight: 660,
+  minHeight: 660,
   columns: [],
   data: [],
-  cellConfig: {
-    height: 40
-  },
-  headerCellConfig: {
-    height: 40
-  },
-  scrollY: { enabled: true, oSize: 20, gt: 30 },
-  stripe: true,
+  scrollY: { enabled: true, oSize: 20, gt: 20, scrollToTopOnChange: true },
+  scrollX: { enabled: true, gt: 10, scrollToLeftOnChange: true },
   emptyText: ' ',
   showHeaderOverflow: true,
   showOverflow: true,
@@ -64,7 +58,7 @@ const tableData = ref<VxeGridProps<any>>({
     backgroundColor: 'var(--color-table-header-bg)'
   },
   columnConfig: { resizable: true, useKey: true },
-  rowConfig: { isHover: true }
+  rowConfig: { isHover: true, isCurrent: true },
 })
 
 const tableRef = ref<VxeGridInstance | null>(null)
@@ -283,7 +277,10 @@ onMounted(() => {
 :deep(.el-icon svg) {
   width: 1em !important;
 }
-
+:deep(.vxe-table--body-inner-wrapper) {
+  min-height: 610px !important;
+  max-height: 610px !important;
+}
 :deep(.vxe-table--empty-placeholder) {
   height: 500px !important;
 }

+ 4 - 0
src/views/Tracking/src/components/TrackingDetail/src/components/EmailDrawer.vue

@@ -138,6 +138,10 @@ const emit = defineEmits<{
 const sendEmail = () => {
   const html = editorRef.value.getHtml()
   const text = editorRef.value.getText()
+  if (!text) {
+    ElMessage.warning('Please enter the email content')
+    return
+  }
   $api
     .sendEmailApi({
       action: 'ocean_order',

+ 2 - 2
vite.config.ts

@@ -50,13 +50,13 @@ export default defineConfig(({ mode }) => {
       })
     ],
     server: {
-      port: 80,
+      port: 8080,
       hmr: true,
       open: true,
       // 设置 https 代理
       proxy: {
         '/api': {
-          target: 'http://192.168.0.161',
+          target: 'http://192.168.14.224',
           changeOrigin: true,
           rewrite: (path: string) => path.replace(/^\/api/, '')
         }

Some files were not shown because too many files changed in this diff