Browse Source

feat:添加booking delivery页面

AmandaG 6 months ago
parent
commit
29983d2164

+ 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')
 }

+ 3 - 1
src/components/AutoSelect/src/AutoSelect.vue

@@ -79,13 +79,14 @@ const remoteMethod = (query: string) => {
     options.value = []
   }
 }
-const emit = defineEmits(['changeAutoSelect'])
+const emit = defineEmits(['changeAutoSelect', 'changeFocus'])
 // 选中改变
 const changeAutoSelect = () => {
   emit('changeAutoSelect', value)
 }
 // 清除警告样式
 const removeClass = () => {
+  emit('changeFocus', true)
   const input_change = document.getElementsByClassName('input_change')
   if (input_change.length) {
     if (input_change[0].classList.value.indexOf('AlertInput') != -1) {
@@ -106,6 +107,7 @@ const removeClass = () => {
       :placeholder="props.ASPlaceholder"
       collapse-tags
       @focus="removeClass"
+      @blur="emit('changeFocus', false)"
       :disabled="props.ASisDisabled"
       collapse-tags-tooltip
       :max-collapse-tags="3"

+ 21 - 4
src/components/DateRange/src/components/CalendarDate.vue

@@ -21,9 +21,25 @@ const props = defineProps({
   isType: {
     type: Boolean,
     default: false
+  },
+  isNeedFooter: {
+    type: Boolean,
+    default: true
+  },
+  isETA: {
+    type: Boolean,
+    default: false
   }
 })
 const ETDDate = ref([])
+const CreatePlaceholder = computed(() => {
+  if (props.isETA) {
+    return ['Start ETA Time', 'End ETA Time']
+  } else {
+    return ['Start ATA Time', 'End ATA Time']
+  }
+
+})
 watch(
   () => props.Date,
   (current: any) => {
@@ -37,7 +53,7 @@ watch(
   { immediate: true, deep: true }
 )
 
-const emit = defineEmits(['DateRangeChange', 'DateChange'])
+const emit = defineEmits(['DateRangeChange', 'DateChange', 'DateChangeFocus'])
 const open = ref(false)
 const Disabled = ref([false, false])
 const isShowExtra = ref(true)
@@ -68,6 +84,7 @@ const ChangeToday = (val: any) => {
   }
 }
 const handleCalendarOpen = (date: any) => {
+  emit('DateChangeFocus', !open.value)
   open.value = !open.value
   if (open.value == false) {
     if (date.length != 2) {
@@ -113,7 +130,7 @@ const isTwoDate = (date: any) => {
 </script>
 <template>
   <div>
-    <div class="ETD_title">{{ props.CalendarTitle }}</div>
+    <div class="ETD_title" v-if="props.CalendarTitle">{{ props.CalendarTitle }}</div>
     <a-range-picker
       separator="To"
       :showToday="false"
@@ -124,7 +141,7 @@ const isTwoDate = (date: any) => {
       :open="open"
       :disabled="Disabled"
       @change="changeRangeData"
-      :placeholder="['Start Time', 'End Time']"
+      :placeholder="isNeedFooter? ['Start Time', 'End Time'] : CreatePlaceholder"
       :format="userStore.dateFormat"
       valueFormat="MM/DD/YYYY"
       @openChange="handleCalendarOpen(ETDDate)"
@@ -138,7 +155,7 @@ const isTwoDate = (date: any) => {
           </svg>
         </span>
       </template>
-      <template #renderExtraFooter v-if="isShowExtra">
+      <template #renderExtraFooter v-if="isShowExtra && isNeedFooter">
         <div class="calender_flex">
           <div class="footer_left">
             <el-button class="el-button--noborder" @click="Earliest">Earliest Time</el-button>

+ 10 - 0
src/components/VBreadcrumb/src/VBreadcrumb.vue

@@ -20,6 +20,16 @@ const jumpLink = (label: string, query: any) => {
     if(label == 'Monitoring Settings') {
       CancelRulesVisible.value = true
       monitoringQuery.value = query
+    } else if(label == 'Destination Delivery') {
+      router.push({
+        name: 'Booking',
+        query: query
+      })
+    } else if(label == 'Configurations') {
+      router.push({
+        name: 'Destination Delivery',
+        query: query
+      })
     } else {
       label &&
       router.push({

+ 10 - 0
src/router/index.ts

@@ -147,6 +147,16 @@ const router = createRouter({
           path: '/Booking/DestinationDelivery',
           name: 'Destination Delivery',
           component: () => import('../views/DesDelivery')
+        },
+        {
+          path: '/Booking/CreateNewBooking',
+          name: 'Create New Booking',
+          component: () => import('../views/CreateNewBooking')
+        },
+        {
+          path: '/Booking/DestinationDelivery/CreateNewRule',
+          name: 'Destination Create New Rule',
+          component: () => import('../views/DesDelivery/src/components/CreateNewRule.vue')
         }
       ]
     }

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

@@ -18,6 +18,8 @@ const whiteList = [
   'Create New Rule',
   'System Message Detail',
   'Destination Delivery',
+  'Configurations',
+  'Destination Create New Rule',
 ]
 
 export const useBreadCrumb = defineStore('breadCrumb', {
@@ -28,9 +30,22 @@ export const useBreadCrumb = defineStore('breadCrumb', {
   actions: {
     setRouteList(toRoute: any) {
       const index = this.routeList.findIndex((item) => item.label === toRoute.name)
-      console.log(toRoute.name)
       if (index !== -1) {
         this.routeList.splice(index + 1)
+        if (toRoute.name === 'Destination Delivery') {
+          this.routeList = [
+            {
+              label: 'Destination Delivery',
+              path: '/booking',
+              query: ''
+            },
+            {
+              label: 'Configurations',
+              path: '/Booking/DestinationDelivery',
+              query: toRoute.query
+            }
+          ]
+        }
       } else if (toRoute.name === 'Public Tracking Detail') {
         this.routeList = [
           {
@@ -83,6 +98,37 @@ export const useBreadCrumb = defineStore('breadCrumb', {
             query: toRoute.query
           }
         ]
+      } else if (toRoute.name === 'Configurations') {
+        this.routeList = [
+          {
+            label: 'Destination Delivery',
+            path: '/booking',
+            query: ''
+          },
+          {
+            label: 'Configurations',
+            path: '/Booking/DestinationDelivery',
+            query: toRoute.query
+          }
+        ]
+      } else if (toRoute.name === 'Destination Create New Rule') {
+        this.routeList = [
+          {
+            label: 'Destination Delivery',
+            path: '/booking',
+            query: ''
+          },
+          {
+            label: 'Configurations',
+            path: '/Booking/DestinationDelivery',
+            query: ''
+          },
+          {
+            label: 'Create New Rule',
+            path: '/Booking/DestinationDelivery/CreateNewRule',
+            query: toRoute.query
+          }
+        ]
       } else if (toRoute.name && whiteList.includes(toRoute.name)) {
         this.routeList.push({
           label: toRoute?.meta?.breadName || toRoute.name,

+ 14 - 0
src/styles/Antdui.scss

@@ -100,4 +100,18 @@
 }
 .ant-picker-dropdown.ant-picker-dropdown-placement-bottomLeft .ant-picker-range-arrow {
   display: none;
+}
+.ant-picker-input input::placeholder {
+  color: var(--color-neutral-3) !important;
+}
+.ant-picker-input input {
+  color: var(--color-neutral-1) !important;
+}
+.ant-picker-cell-inner {
+  display: flex !important;
+  align-items: center !important;
+  justify-content: center !important;
+  width: 32px !important;
+  height: 32px !important;
+  border-radius: 6px !important;
 }

+ 17 - 0
src/styles/elementui.scss

@@ -17,6 +17,7 @@
 
 .el-button.el-button--noborder--configuration {
   border: none;
+  background-color: transparent;
   span {
     color: var(--color-theme);
   }
@@ -29,6 +30,9 @@
     }
   }
 }
+.el-button.el-button--noborder--configuration.is-disabled {
+  opacity: 0.3;
+}
 
 button.el-button.el-button--text {
   height: 24px;
@@ -159,6 +163,19 @@ button.el-button--main.is-disabled {
     }
   }
 }
+.el-button.el-button--grey.is-disabled {
+  border: none;
+  background-color: var(--color-grey);
+  color: var(--color-neutral-1);
+  fill: var(--color-neutral-1);
+  &:hover {
+    background-color: var(--color-grey);
+    fill: var(--color-neutral-1);
+    span {
+      color: var(--color-neutral-1);
+    }
+  }
+}
 
 .el-button.el-button--blue {
   border: none;

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

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

+ 645 - 0
src/views/CreateNewBooking/src/CreateNewbooking.vue

@@ -0,0 +1,645 @@
+<script setup lang="ts">
+import CalendarDate from '@/components/DateRange/src/components/CalendarDate.vue'
+import AutoSelect from '@/components/AutoSelect/src/AutoSelect.vue'
+import NewbookingTable from './components/NewbookingTable.vue'
+import AddNewAddress from './images/default_add_address@2x.png'
+import NotAvailable from './images/default_destination_not_available@2x.png'
+import NotShipment from './images/default_no_shipment@2x.png'
+import { useUserStore } from '@/stores/modules/user'
+
+const userStore = useUserStore()
+
+const CreateNewBOokingSearch = ref('')
+const VesselName = ref([])
+const isFocused = ref(false)
+const isataFocused = ref(false)
+const isetaFocused = ref(false)
+const ManageVisible = ref(false)
+const AddNewAddressVisible = ref(false)
+const NoPermissionVisible = ref(false)
+const NoEligibleVisible = ref(true)
+const ATATimeList = ref(null)
+const ETATimeList = ref(null)
+const Addressradio = ref()
+const LocationName = ref('')
+const AddressLine1 = ref('')
+const AddressLine2 = ref('')
+const PostalCode = ref('')
+const ContactPerson = ref('')
+const ContactNumber = ref('')
+const instructions = ref('')
+const NewBookingColumnsList = ref([
+  {
+    field: 'HBOLNo',
+    title: 'HBOL No.',
+    type: '',
+    formatter: ''
+  },
+  {
+    field: 'ContainerNo',
+    title: 'Container No.',
+    type: '',
+    formatter: ''
+  },
+  {
+    field: 'ServiceType',
+    title: 'Service Type',
+    type: '',
+    formatter: ''
+  },
+  {
+    field: 'ETD',
+    title: 'ETD',
+    type: '',
+    formatter: 'date'
+  },
+  {
+    field: 'ETA',
+    title: 'ETA',
+    type: '',
+    formatter: 'date'
+  }
+])
+const modetypeValue = ref('Truck')
+const Requirements = ref('')
+const ModeType = ref([
+  {
+    label: 'Truck',
+    value: 'Truck'
+  }
+])
+
+// 设置无法点击
+const isNotClickAddress = computed(() => {
+  return NoPermissionVisible.value || NoEligibleVisible.value
+})
+
+const ManageAddressList = ref([
+  {
+    title: 'Main Distribution Center',
+    Address1: '160#BEIJING ROAD, JINGAN District,',
+    Address2: 'Shenzhen, China',
+    Contact: 'Contact:  John Doe (+65 9123 4567)'
+  },
+  {
+    title: 'Main Distribution Center2',
+    Address1: '160#BEIJING ROAD, JINGAN District,',
+    Address2: 'Shenzhen, China',
+    Contact: 'Contact:  John Doe (+65 9123 4567)'
+  },
+  {
+    title: 'Main Distribution Center3',
+    Address1: '160#BEIJING ROAD, JINGAN District,',
+    Address2: 'Shenzhen, China',
+    Contact: 'Contact:  John Doe (+65 9123 4567)'
+  }
+])
+// 需要特殊样式的日期列表
+const specialDates = ref([
+  '2025-05-27',
+  '2025-05-28', 
+]);
+
+
+const DateRangeChangeETA = (val:any) => {
+  ETATimeList.value = val.data
+}
+
+const DateRangeChangeATA = (val:any) => {
+  ATATimeList.value = val.data
+}
+
+const showLabel = computed(() => {
+  return VesselName.value.length!= 0 || isFocused.value
+})
+
+const showataLabel = computed(() => {
+  return ATATimeList.value != null || isataFocused.value
+})
+
+const showETAlabel = computed(() => {
+  return ETATimeList.value != null || isetaFocused.value
+})
+
+const changeFocus = (val: boolean) => {
+  isFocused.value = val
+}
+
+const DateChangeFocusATA = (val: boolean) => {
+  isataFocused.value = val
+}
+const DateChangeFocusETA = (val: boolean) => {
+  isetaFocused.value = val
+}
+
+// 特殊日期样式
+const getCurrentStyle = (current: any) => {
+  const dateString = current.format('YYYY-MM-DD');
+  if (specialDates.value.includes(dateString)) {
+    return { 
+      background: '#b3e5d4', 
+      borderRadius: '6px',
+      color: `var(--color-neutral-1)`
+    };
+  }
+  
+  // 默认样式
+  return {};
+}
+
+// 点击按钮
+const handleclickbutton = (val: any) => {
+  Requirements.value = Requirements.value + val
+}
+
+const selectedAddressList = ref()
+const isselectedAddress = ref(null)
+const changeAddressRadio = () => {
+  ManageVisible.value = false
+  if(Addressradio.value != undefined) {
+    isselectedAddress.value = Addressradio.value
+
+  }
+  selectedAddressList.value = ManageAddressList.value[Addressradio.value]
+}
+</script>
+
+<template>
+  <div>
+    <div class="Title">
+      <div>Create New Booking</div>
+      <div class="flex">
+        <el-button class="el-button--default create-button"><span class="font_family icon-icon_return_b"></span> Cancel</el-button>
+        <el-button :disabled="isNotClickAddress" class="el-button--main create-button"><span class="font_family icon-icon_submit_b"></span> Submit</el-button>
+      </div>
+    </div>
+    <!-- Select Shipments -->
+    <div class="select_shipments">
+      <div><span class="stars_red">*</span>Select Shipments</div>
+      <div class="flex shipments_search">
+        <el-input
+          placeholder="Enter Booking/HBL/PO/Carrier Booking No. "
+          v-model="CreateNewBOokingSearch"
+          class="log_input"
+        >
+          <template #prefix>
+            <span class="iconfont_icon">
+              <svg class="iconfont icon_search" aria-hidden="true">
+                <use xlink:href="#icon-icon_search_b"></use>
+              </svg>
+            </span>
+          </template>
+        </el-input>
+        <div class="input-with-label">
+          <CalendarDate style="margin: 0 8px;" :isNeedFooter="false" :isETA="true" @DateRangeChange="DateRangeChangeETA" @DateChangeFocus="DateChangeFocusETA"></CalendarDate>
+          <span v-if="showETAlabel" class="border-label">ETA</span>
+        </div>
+        <div class="input-with-label">
+          <CalendarDate :isNeedFooter="false" @DateRangeChange="DateRangeChangeATA" @DateChangeFocus="DateChangeFocusATA"></CalendarDate>
+          <span v-if="showataLabel" class="border-label">ATA</span>
+        </div>
+        <div class="input-with-label" style="margin: 0 8px;">
+          <AutoSelect ASPlaceholder="Input Vessel Name" :ASValue="VesselName" @changeFocus="changeFocus"></AutoSelect>
+          <span v-if="showLabel" class="border-label">Vessel Name</span>
+        </div>
+        <el-button class="el-button--dark create-button">Search</el-button>
+      </div>
+      <NewbookingTable :ColumnsList="NewBookingColumnsList"></NewbookingTable>
+    </div>
+    <!-- Delivery Information -->
+    <div class="Delivery">Delivery Information</div>
+    <div class="delivery_address">
+      <div class="deliverty_flex">
+        <div><span class="stars_red">*</span>Delivery Address</div>
+        <el-button :disabled="isNotClickAddress" @click="ManageVisible = true" class="el-button--noborder--configuration"><span class="font_family icon-icon_location_b"></span> Address Book</el-button>
+      </div>
+      <div class="empty_address" v-if="isselectedAddress == null">
+        <img :src="AddNewAddress" width="48" style="margin-bottom: 8px;" />
+        <el-button :disabled="isNotClickAddress" class="el-button--main" @click="AddNewAddressVisible = true"> + Add New Address</el-button>
+      </div>
+      <div v-else class="addressradio">
+        <el-radio>
+          <div>
+            <div class="radio_top">
+              <div class="radio_title">{{ selectedAddressList.title }}</div>
+            </div>
+            <div class="radio_content">
+              <div class="radio_content_text">{{ selectedAddressList.Address1 }}</div>
+              <div class="radio_content_text">{{ selectedAddressList.Address2 }}</div>
+            </div>
+            <div class="radio_bottom radio_content_text">
+              {{ selectedAddressList.Contact }}
+            </div>
+          </div>
+        </el-radio>
+      </div>
+      <div class="delivery_type">
+        <div style="margin-right: 12px;">
+          <div class="delivery_type_title"><span class="stars_red">*</span>Mode Type</div>
+          <el-select
+            v-model="modetypeValue"
+            placeholder="Select"
+            :disabled="isNotClickAddress"
+            style="width: 240px"
+          >
+            <el-option
+              v-for="item in ModeType"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </div>
+        <div>
+        <div class="delivery_type_title"><span class="stars_red">*</span>Preferred Delivery Date</div>
+        <a-date-picker
+          :disabled="isNotClickAddress"
+          :showToday="false"
+          style="width: 240px;"
+          :format="userStore.dateFormat"
+          placeholder="Please Select Date"
+          valueFormat="MM/DD/YYYY"
+        >
+          <template #renderExtraFooter>
+            <div class="recommended"><div class="recommend_color"></div>Recommended delivery date</div>
+          </template>
+          <template #dateRender="{ current }">
+            <div class="ant-picker-cell-inner" :style="getCurrentStyle(current)">
+              {{ current.date() }}
+            </div>
+          </template>
+        </a-date-picker>
+      </div>
+      </div>
+      <div class="delivery_type_title">Special Requirements</div>
+      <div class="flex" style="margin-top: 8px;">
+        <el-button :disabled="isNotClickAddress" class="el-button--grey" @click="handleclickbutton('Tail Lift Required')">Tail Lift Required</el-button>
+        <el-button :disabled="isNotClickAddress" class="el-button--grey" @click="handleclickbutton('Side Loading')">Side Loading</el-button>
+        <el-button :disabled="isNotClickAddress" class="el-button--grey" @click="handleclickbutton('Forklift Required')">Forklift Required</el-button>
+        <el-button :disabled="isNotClickAddress" class="el-button--grey" @click="handleclickbutton('Special Equipment')">Special Equipment</el-button>
+      </div>
+      <el-input :disabled="isNotClickAddress" v-model="Requirements" placeholder="Eenter any additional requirements or notes..." type="textarea" style="margin-top: 8px;" :rows="4"></el-input>
+    </div>
+    <el-dialog
+      v-model="ManageVisible"
+      width="640"
+      class="ManageDialog"
+      :show-close="false"
+    >
+      <el-radio-group v-model="Addressradio">
+        <el-radio v-for="(item, index) in ManageAddressList" :key="index" :value="index">
+          <div class="addressradio">
+            <div class="radio_top">
+              <div class="radio_title">{{ item.title }}</div>
+              <div>
+                <el-button class="el-button--blue"><span class="font_family icon-icon_edit_b"></span></el-button>
+                <el-button class="el-button--blue"><span class="font_family icon-icon_delete_b"></span></el-button>
+              </div>
+            </div>
+            <div class="radio_content">
+              <div class="radio_content_text">{{ item.Address1 }}</div>
+              <div class="radio_content_text">{{ item.Address2 }}</div>
+            </div>
+            <div class="radio_bottom radio_content_text">
+              {{ item.Contact }}
+            </div>
+          </div>
+        </el-radio>
+      </el-radio-group>
+      <template #header>
+        <div class="my-header">
+          <div class="header_Title">Manage Address</div>
+          <el-button  @click="AddNewAddressVisible = true" class="el-button--noborder--configuration">+ Add New Address</el-button>
+        </div>
+      </template>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button class="el-button--default dialog-button" @click="ManageVisible = false">Cancel</el-button>
+          <el-button class="el-button--dark dialog-button" @click="changeAddressRadio">
+            OK
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog
+      v-model="AddNewAddressVisible"
+      width="640"
+      title="Add New Delivery Address"
+      :show-close="false"
+    >
+      <div class="diaolog_add_title"><span class="stars_red">*</span>Location Name</div>
+      <el-input class="inputmargin" placeholder="Enter location name" v-model="LocationName"></el-input>
+      <div class="diaolog_add_title"><span class="stars_red">*</span>Address Line 1</div>
+      <el-input class="inputmargin" placeholder="Street address, P.O. Box, or company name" v-model="AddressLine1"></el-input>
+      <div class="diaolog_add_title">Address Line 2</div>
+      <el-input class="inputmargin" placeholder="Unit number, floor, etc. (optional)" v-model="AddressLine2"></el-input>
+      <div class="flex">
+        <div style="margin-right: 9px;width: 50%;">
+          <div class="diaolog_add_title"><span class="stars_red">*</span>Country/City</div>
+          <el-input class="inputmargin" placeholder="Unit number, floor, etc. (optional)" v-model="AddressLine2"></el-input>
+        </div>
+        <div style="width: 50%;">
+          <div class="diaolog_add_title"><span class="stars_red">*</span>Postal Code</div>
+          <el-input class="inputmargin" placeholder="Enter postal code" v-model="PostalCode"></el-input>
+        </div>
+      </div>
+      <div class="diaolog_add_title_bold">Contact Information</div>
+      <div class="flex">
+        <div style="margin-right: 9px;width: 50%;">
+          <div class="diaolog_add_title"><span class="stars_red">*</span>Contact Person</div>
+          <el-input class="inputmargin" placeholder="Name" v-model="ContactPerson"></el-input>
+        </div>
+        <div style="width: 50%;">
+          <div class="diaolog_add_title"><span class="stars_red">*</span>Contact Number</div>
+          <el-input class="inputmargin" placeholder="Mobile Numer" v-model="ContactNumber"></el-input>
+        </div>
+      </div>
+      <div class="diaolog_add_title" style="margin-bottom: 4px;">Additional Notes</div>
+      <el-input type="textarea" :rows="3" placeholder="Ddelivery instructions, landmarks,etc. (optional)" v-model="instructions"></el-input>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button class="el-button--default dialog-button" @click="AddNewAddressVisible = false">Cancel</el-button>
+          <el-button class="el-button--dark dialog-button" @click="AddNewAddressVisible = false">
+            Save Address
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 无站点权限弹框 -->
+    <el-dialog
+      v-model="NoPermissionVisible"
+      :show-close="false"
+      align-center
+      class="notDialog"
+      width="480"
+    >
+      <div class="flex_center">
+        <img :src="NotAvailable" width="100" />
+        <div class="alert_title">Destination Delivery Service Not Available</div>
+        <div class="alert_text">Destination delivery service is not yet available for your shipment destinations. To request this service, please contact the destination office first.</div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button style="width: 80px;" class="el-button--dark" @click="NoPermissionVisible = false">OK</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 无符合条件弹框 -->
+    <el-dialog
+      v-model="NoEligibleVisible"
+      :show-close="false"
+      align-center
+      class="notDialog"
+      width="480"
+    >
+      <div class="flex_center">
+        <img :src="NotShipment" width="100" />
+        <div class="alert_title">No Eligible Shipments for Booking</div>
+        <div class="alert_text">Your shipments are currently outside the booking window. Eligible shipments will appear here when booking window opens.</div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button style="width: 80px;" class="el-button--dark" @click="NoEligibleVisible = false">OK</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.Title {
+  display: flex;
+  height: 68px;
+  border: 1px solid var(--color-border);
+  border-top: none;
+  border-width: 1px 0 1px 0;
+  font-size: var(--font-size-6);
+  font-weight: 700;
+  padding: 0 24px;
+  align-items: center;
+  justify-content: space-between;
+}
+.flex {
+  display: flex;
+}
+.deliverty_flex {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 4px;
+}
+.con-icon_submit_b {
+  transform: rotate(-45deg);
+}
+.create-button {
+  width: 115px;
+  height: 40px;
+}
+.select_shipments {
+  border: 1px solid var(--color-border);
+  margin: 32px 24px 12px 24px;
+  padding: 9px 16px 16px 16px;
+  border-radius: 12px;
+}
+.delivery_address {
+  border: 1px solid var(--color-border);
+  margin: 0 24px 12px 24px;
+  padding: 9px 16px 16px 16px;
+  border-radius: 12px;
+}
+.stars_red {
+  color: var(--color-danger);
+}
+.shipments_search {
+  margin: 16px 0 8px 0;
+}
+:deep(.log_input .el-input__wrapper) {
+  box-shadow: 0 0 0 1px var(--color-select-border) inset;
+  border-radius: 6px;
+}
+.input-with-label {
+  position: relative;
+  display: inline-block;
+  width: 35%;
+}
+.border-label {
+  position: absolute;
+  top: -7px;
+  left: 10px;
+  background: white; /* 用背景色覆盖边框 */
+  padding: 0 5px;
+  font-size: 12px;
+  color: var(--color-neutral-2);
+  z-index: 1;
+}
+.vessel_input {
+  height: 40px;
+  width: 100%;
+}
+:deep(.el-select__wrapper.is-filterable) {
+  height: 40px;
+}
+.Delivery {
+  font-size: 18px;
+  font-weight: 700;
+  margin: 11px 0 14px 24px;
+}
+.empty_address {
+  height: 122px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  border-radius: 12px;
+  border: 2px dashed #ED6D00;
+  background: rgba(237, 109, 0, 0.05);
+}
+.delivery_type {
+  display: flex;
+  margin: 16px 0;
+}
+.delivery_type_title {
+  font-size: 14px;
+  font-weight: 700;
+  margin-bottom: 4px;
+}
+.recommended {
+  background: #F5F7FA;
+  border-bottom: 1px solid var(--color-border);
+  height: 24px;
+  display: flex;
+  align-items: center;
+  padding-left: 16px;
+  font-size: 10px;
+  color: var(--color-neutral-2);
+}
+.recommend_color {
+  width: 8px;
+  height: 8px;
+  border-radius: 1px;
+  opacity: 0.3;
+  background: #00A870;
+  margin-right: 4px;
+}
+.reco_button {
+  height: 56px;
+  display: flex;
+  align-items: center;
+  justify-content: end;
+  padding: 0 16px;
+}
+.recomend-button {
+  width: 80px;
+  height: 40px;
+}
+.el-button--grey {
+  padding: 10px 16px;
+  border-radius: 15px;
+}
+:deep(.el-textarea) {
+  .el-textarea__inner {
+    resize: none; // 去除右下角图标
+    padding: 5px 7px 5px 10px;
+    box-shadow: 0 0 0 1px var(--color-select-border) inset;
+    color: var(--color-neutral-1);
+  }
+}
+.dialog-button {
+  width: 115px;
+  height: 40px;
+}
+.my-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  height: 48px;
+}
+:deep(.ManageDialog .el-dialog__header) {
+  padding: 0 16px;
+  height: 48px;
+}
+.header_Title {
+  font-size: 18px;
+}
+.addressradio {
+  background-color: #F8F9FD;
+  border-radius: 12px;
+  padding: 13px 8px 16px 16px;
+}
+.radio_top {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.radio_title {
+  font-size: 14px;
+  font-weight: 700;
+}
+.radio_content_text {
+  color: var(--color-neutral-2);
+  font-size: 14px;
+  height: 21px;
+}
+.radio_content {
+  margin-bottom: 8px;
+}
+:deep(.el-radio-group) {
+  display: block;
+}
+:deep(.el-radio) {
+  display: flex;
+  min-height: 32px;
+  margin-bottom: 8px;
+  border-radius: 12px;
+  margin-right: 0;
+  height: fit-content;
+  line-height: 32px;
+  align-items: baseline;
+}
+.el-button--blue {
+  width: 24px;
+  height: 24px;
+}
+.diaolog_add_title {
+  color: var(--color-neutral-2);
+  font-size: 12px;
+}
+:deep(.el-input__wrapper) {
+  height: 40px;
+}
+.inputmargin {
+  margin: 4px 0 16px 0;
+}
+.diaolog_add_title_bold {
+  color: var(--color-neutral-1);
+  font-size: 14px;
+  font-weight: 700;
+  margin-bottom: 11px;
+}
+.dialog-footer {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.flex_center {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  padding: 0 19px;
+}
+.alert_title {
+  text-align: center;
+  font-size: 18px;
+  font-weight: 700;
+}
+.alert_text {
+  text-align: center;
+  font-size: 14px;
+  line-height: 21px;
+  margin-top: 8px;
+}
+:deep(.notDialog .el-dialog__header) {
+  display: none;
+}
+</style>

+ 202 - 0
src/views/CreateNewBooking/src/components/NewbookingTable.vue

@@ -0,0 +1,202 @@
+<script setup lang="ts">
+import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+import { useRowClickStyle } from '@/hooks/rowClickStyle'
+import { formatTimezone } from '@/utils/tools'
+import { ref, onMounted } from 'vue'
+
+interface ColumnsListItem {
+  field: String
+  title: String
+  type: String
+  formatter: String
+}
+const props = defineProps({
+  ColumnsList: Array<ColumnsListItem>
+})
+
+const columnstest = ref(props.ColumnsList)
+const tableLoadingTable = ref(false)
+const isNotActivated = ref(false)
+
+const tableData = ref<VxeGridProps<any>>({
+  border: true,
+  round: true,
+  maxHeight: 288,
+  columns: [],
+  data: [],
+  scrollY: { enabled: true, oSize: 20, gt: 30 },
+  emptyText: ' ',
+  showHeaderOverflow: true,
+  showOverflow: true,
+  headerRowStyle: {
+    backgroundColor: 'var(--color-table-header-bg)'
+  },
+  columnConfig: { resizable: true, useKey: true },
+  rowConfig: { isHover: true, isCurrent: true },
+})
+
+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) => formatTimezone(cellValue)
+      }
+    }  else if (item.type === 'link') {
+      curColumn = {
+        ...curColumn,
+        slots: { default: 'trackingNo' }
+      }
+    }
+    return curColumn
+  })
+  return newColumns
+}
+// 获取表格列
+const getTableColumns = async () => {
+  tableData.value.columns = handleColumns(columnstest.value)
+  tableData.value.columns?.unshift({
+    type: 'checkbox',
+    width: 40
+  })
+}
+// 获取表格数据
+const getTableData = (val: any) => {
+  // tableData.value.data = val.setTrackingTableData
+  tableData.value.data = [
+    // {
+    //   HBOLNo: 'SHIP123456',
+    //   ContainerNo: 'CONT789012',
+    //   ServiceType: 'CY/CY',
+    //   ETD: '2024-01-01',
+    //   ETA: '2024-01-01'
+    // },
+    // {
+    //   HBOLNo: 'SHIP1234562',
+    //   ContainerNo: 'CONT789012',
+    //   ServiceType: 'CY/CY',
+    //   ETD: '2024-01-01',
+    //   ETA: '2024-01-01'
+    // },
+    // {
+    //   HBOLNo: 'SHIP1234563',
+    //   ContainerNo: 'CONT789012',
+    //   ServiceType: 'CY/CY',
+    //   ETD: '2024-01-01',
+    //   ETA: '2024-01-01'
+    // },
+    // {
+    //   HBOLNo: 'SHIP1234564',
+    //   ContainerNo: 'CONT789012',
+    //   ServiceType: 'CY/CY',
+    //   ETD: '2024-01-01',
+    //   ETA: '2024-01-01'
+    // },
+    // {
+    //   HBOLNo: 'SHIP1234565',
+    //   ContainerNo: 'CONT789012',
+    //   ServiceType: 'CY/CY',
+    //   ETD: '2024-01-01',
+    //   ETA: '2024-01-01'
+    // },
+    // {
+    //   HBOLNo: 'SHIP1234566',
+    //   ContainerNo: 'CONT789012',
+    //   ServiceType: 'CY/CY',
+    //   ETD: '2024-01-01',
+    //   ETA: '2024-01-01'
+    // }
+  ]
+}
+
+// 选中
+const selectChangeEvent = () => {
+  const $grid = tableRef.value
+  if ($grid) {
+    const records = $grid.getCheckboxRecords()
+    console.log(records)
+  }
+}
+// 全选
+const selectAllChangeEvent= () => {
+  const $grid = tableRef.value
+  if ($grid) {
+    const records = $grid.getCheckboxRecords()
+    console.log(records)
+  }
+}
+
+// 实现行点击样式
+useRowClickStyle(tableRef)
+
+onMounted(() => {
+  getTableColumns()
+  getTableData(1)
+})
+defineExpose({
+  getTableData
+})
+
+</script>
+
+<template>
+  <div class="SettingTable">
+    <vxe-grid
+      ref="tableRef"
+      :style="{ border: 'none' }"
+      v-bind="tableData"
+      v-vloading="tableLoadingTable"
+      @checkbox-change="selectChangeEvent"
+      @checkbox-all="selectAllChangeEvent"
+    >
+      <template #empty>
+        <div v-if="isNotActivated" class="empty-text">
+          This service isn't activated yet. Please contact our team to enable it.
+        </div>
+        <div v-else class="empty-text">
+          No eligible shipments found to create a new booking.
+        </div>
+      </template>
+    </vxe-grid>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.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;
+}
+:deep(.el-icon svg) {
+  width: 1em !important;
+}
+:deep(.vxe-table--render-default.is--round .vxe-table--border-line) {
+  border-radius: 6px;
+}
+.empty-text {
+  font-size: 14px;
+  font-weight: 400;
+  color: var(--color-neutral-1);
+  margin: 31px 0;
+}
+</style>

BIN
src/views/CreateNewBooking/src/images/default_add_address@2x.png


BIN
src/views/CreateNewBooking/src/images/default_destination_not_available@2x.png


BIN
src/views/CreateNewBooking/src/images/default_no_shipment@2x.png


+ 68 - 2
src/views/DesDelivery/src/DesDelivery.vue

@@ -1,3 +1,69 @@
+<script setup lang="ts">
+import ConfigurationsTable from './components/ConfigurationsTable.vue'
+import { useCalculatingHeight } from '@/hooks/calculatingHeight'
+
+const filterRef: Ref<HTMLElement | null> = ref(null)
+
+const AddRulesTableColumns = ref([
+  {
+    field: 'Event',
+    title: 'Event',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'Shipment Range',
+    title: 'Shipment Range',
+    type: 'normal',
+    formatter: ''
+  },{
+    field: 'Event Details',
+    title: 'Event Details',
+    type: 'normal',
+    formatter: ''
+  },
+  {
+    field: 'Frequency',
+    title: 'Frequency',
+    type: 'normal',
+    formatter: ''
+  },{
+    field: 'Method',
+    title: 'Method',
+    type: 'normal',
+    formatter: ''
+  }
+])
+const containerHeight = useCalculatingHeight(document.documentElement, 206, [filterRef])
+
+</script>
+
 <template>
-  <div>123</div>
-</template>
+  <div>
+    <div class="Title">Configuration</div>
+    <div class="AddRules">
+      Added Rules
+      <ConfigurationsTable :height="containerHeight" :ColumnsList="AddRulesTableColumns"></ConfigurationsTable>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.Title {
+  display: flex;
+  height: 68px;
+  border: 1px solid var(--color-border);
+  border-top: none;
+  border-width: 1px 0 1px 0;
+  font-size: var(--font-size-6);
+  font-weight: 700;
+  padding: 0 24px;
+  align-items: center;
+  justify-content: space-between;
+}
+.AddRules {
+  font-size: 18px;
+  font-weight: 700;
+  padding: 37px 0 13px 24px;
+}
+</style>

+ 222 - 0
src/views/DesDelivery/src/components/ConfigurationsTable.vue

@@ -0,0 +1,222 @@
+<script setup lang="ts">
+import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
+import { useRowClickStyle } from '@/hooks/rowClickStyle'
+import { formatTimezone } from '@/utils/tools'
+import { ref, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { useVisitedRowState } from '@/stores/modules/visitedRow'
+
+const visitedRowState = useVisitedRowState()
+const router = useRouter()
+interface ColumnsListItem {
+  field: String
+  title: String
+  type: String
+  formatter: String
+}
+const props = defineProps({
+  ColumnsList: Array<ColumnsListItem>,
+  height: Number
+})
+
+const columnstest = ref(props.ColumnsList)
+
+const tableData = ref<VxeGridProps<any>>({
+  border: true,
+  round: true,
+  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, isCurrent: true },
+})
+
+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) => formatTimezone(cellValue)
+      }
+    }  else if (item.type === 'link') {
+      curColumn = {
+        ...curColumn,
+        slots: { default: 'trackingNo' }
+      }
+    }
+    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 = (val: any) => {
+  tableData.value.data = val.setTrackingTableData
+}
+
+// 实现行点击样式
+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)
+      }
+    })
+}
+
+// 点击link字段
+const handleLinkClick = (row: any) => {
+  router.push({
+    path: '/shipment/detail',
+    query: { a: row.__serial_no, _schemas: row._schemas }
+  })
+  sessionStorage.setItem('activeTab', 'Subscribe Notifications')
+  visitedRowState.setTrackingTableData(row['__serial_no'])
+}
+
+onMounted(() => {
+  getTableColumns()
+})
+defineExpose({
+  getTableData
+})
+
+const clickAddNewRule = () => {
+  router.push({
+    path: '/Booking/DestinationDelivery/CreateNewRule',
+    query: {}
+  })
+}
+const getpaginationTableData = () => {
+  $api
+    .SubscribePagination({
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        tableData.value.data = res.data.tableData
+      }
+    })
+}
+</script>
+
+<template>
+  <div class="SettingTable">
+    <vxe-grid
+      ref="tableRef"
+      :style="{ border: 'none' }"
+      :height="props.height"
+      v-bind="tableData"
+    >
+      <template #empty>
+        <div>
+          <div class="empty-text">
+            Configure available destination delivery regions and time slots.
+          </div>
+          <el-button class="el-button--main" style="width: 117px; height: 40px;" @click="clickAddNewRule">+ Add Rule</el-button>
+        </div>
+      </template>
+      <!-- Tracking No字段的插槽 -->
+      <template #trackingNo="{ row, column }">
+        <span
+          style="color: var(--color-theme); cursor: pointer"
+          @click="handleLinkClick(row)"
+        >
+          {{ row[column.field] }}
+        </span>
+      </template>
+      <template #action="{ row }">
+        <el-button class="el-button--blue" style="height: 24px">
+          <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 Rule
+          </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">
+              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>
+</template>
+
+<style lang="scss" scoped>
+.SettingTable {
+  padding: 13px 24px 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;
+}
+:deep(.el-icon svg) {
+  width: 1em !important;
+}
+:deep(.vxe-table--render-default.is--round .vxe-table--border-line) {
+  border-radius: 6px;
+}
+.empty-text {
+  font-size: 14px;
+  font-weight: 400;
+  color: var(--color-neutral-2);
+  margin: 8px 0;
+}
+</style>

+ 229 - 0
src/views/DesDelivery/src/components/CreateNewRule.vue

@@ -0,0 +1,229 @@
+<script setup lang="ts">
+import SelectStation from './SelectStation.vue'
+import SetBookingWindow from './SetBookingWindow.vue'
+import RecommendDate from './RecommendDate.vue'
+
+const RulesActive = ref(['SelectStation', 'SelectBooking', 'KLNPLC', 'RecommendDeliveryDate'])
+const IsFirstActive = ref(true)
+const IsTwoActive = ref(true)
+const IsFourActive = ref(true)
+const IsThreeActive = ref(true)
+const KLNPLCvalue = ref([])
+
+const CountryCheckedList = ref([])
+const CountryCheckboxList = ref([
+  { value: 'China', label: 'China' },
+  { value: 'USA', label: 'USA' },
+  { value: 'Japan', label: 'Japan' },
+  { value: 'Korea', label: 'Korea' },
+  { value: 'Taiwan', label: 'Taiwan' },
+  { value: 'Hong Kong', label: 'Hong Kong' },
+  { value: 'Hong Kong2', label: 'Hong Kong2' },
+  { value: 'Hong Kong3', label: 'Hong Kong3' },
+  { value: 'Hong Kong4', label: 'Hong Kong4' },
+  { value: 'Hong Kong5', label: 'Hong Kong5' },
+  { value: 'Hong Kong6', label: 'Hong Kong6' },
+  { value: 'Hong Kong7', label: 'Hong Kong7' },
+])
+
+const KLNPLoptions = [
+  {
+    value: 'Option1',
+    label: 'Option1',
+  },
+  {
+    value: 'Option2',
+    label: 'Option2',
+  },
+  {
+    value: 'Option3',
+    label: 'Option3',
+  },
+  {
+    value: 'Option4',
+    label: 'Option4',
+  },
+  {
+    value: 'Option5',
+    label: 'Option5',
+  },
+]
+
+// select country
+const handleClickSelectCountry = (val:any) =>{
+  console.log(val)
+}
+</script>
+
+<template>
+  <div>
+    <div class="Title">Create New Rule</div>
+    <div class="setting-content">
+      <div class="setting-top-title">Setting</div>
+      <el-collapse v-model="RulesActive" @change="IsFirstActive = !IsFirstActive">
+        <el-collapse-item name="SelectStation">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon icon_dark">
+                  <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 Station (Enable Booking)
+              </div>
+            </template>
+            <div>
+              <SelectStation @handleClickSelectCountry="handleClickSelectCountry"
+              :CheckboxList="CountryCheckboxList"
+              :CheckedList="CountryCheckedList"
+              ></SelectStation>
+            </div>
+          </el-collapse-item>
+      </el-collapse>
+      <el-collapse v-model="RulesActive" @change="IsTwoActive = !IsTwoActive">
+        <el-collapse-item name="SelectBooking">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon icon_dark">
+                  <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>Set Booking Window
+              </div>
+            </template>
+            <div>
+              <SetBookingWindow></SetBookingWindow>
+            </div>
+          </el-collapse-item>
+      </el-collapse>
+      <el-collapse v-model="RulesActive" @change="IsThreeActive = !IsThreeActive">
+        <el-collapse-item name="RecommendDeliveryDate">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon icon_dark">
+                  <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>Recommend Delivery Date 
+              </div>
+            </template>
+            <div>
+              <RecommendDate></RecommendDate>
+            </div>
+          </el-collapse-item>
+      </el-collapse>
+      <el-collapse v-model="RulesActive" @change="IsFourActive = !IsFourActive">
+        <el-collapse-item name="KLNPLC">
+            <template #title>
+              <div class="Rules_Title">
+                <span class="iconfont_icon icon_dark">
+                  <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>KLN PLC
+              </div>
+            </template>
+            <div>
+              <el-select
+                v-model="KLNPLCvalue"
+                filterable
+                multiple
+                placeholder="Select Employee Account"
+                style="width: 400px; height: 40px;"
+              >
+                <el-option
+                  v-for="item in KLNPLoptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </div>
+          </el-collapse-item>
+      </el-collapse>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.Title {
+  display: flex;
+  height: 68px;
+  border: 1px solid var(--color-border);
+  border-top: none;
+  border-width: 1px 0 1px 0;
+  font-size: var(--font-size-6);
+  font-weight: 700;
+  padding: 0 24px;
+  align-items: center;
+  justify-content: space-between;
+}
+.setting-content {
+  margin: 16px 24px 48px 24px;
+  padding-bottom: 24px;
+  border: 1px solid var(--color-system-border-1);
+  border-radius: 12px;
+}
+.setting-top-title {
+  background-color: var(--color-shipment-status-header-bg);
+  height: 48px;
+  padding: 0 16px;
+  display: flex;
+  align-items: center;
+  border-radius: 12px 12px 0 0;
+}
+.Rules_Title {
+  font-size: 14px;
+  font-weight: 700;
+}
+.stars_red {
+  color: var(--color-danger);
+}
+:deep(.el-collapse) {
+  border: none;
+  padding: 0 16px;
+}
+:deep(.el-collapse-item__header) {
+  border: none !important;
+}
+:deep(.el-collapse-item__arrow) {
+  width: 0 !important;
+  height: 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(.el-collapse-item__wrap) {
+  border-bottom: none;
+}
+.iconfont_icon {
+  margin-right: 8px;
+}
+:deep(.el-select__wrapper) {
+  box-shadow: none;
+  border: 1px solid var(--color-select-border);
+}
+:deep(.el-select__wrapper.is-hovering:not(.is-focused)) {
+  box-shadow: none;
+  border: 1px solid var(--color-theme);
+}
+:deep(.el-select__wrapper.is-focused ){
+  box-shadow: none;
+  border: 1px solid var(--color-theme);
+}
+</style>

+ 557 - 0
src/views/DesDelivery/src/components/RecommendDate.vue

@@ -0,0 +1,557 @@
+<script setup lang="ts">
+const Recommendradio = ref(0)
+const isRecommendETA = ref(false)
+const isAir = ref(false)
+const isSea = ref(false)
+const RecommendCheckedList = ref([])
+const RuleTypeoptions = ref([
+  {
+    label: 'Default Rule',
+    value: 'Default Rule'
+  },
+  {
+    label: 'Specific Rule',
+    value: 'Specific Rule'
+  },
+  {
+    label: 'Single Dimension',
+    value: 'Single Dimension'
+  }
+])
+const AirPortoptions = ref([])
+const AirContentList = ref([
+  {
+    Priority: 'P1',
+    RuleType: 'Default Rule',
+    AirPort: 'All',
+    FromDate: '',
+    ToDate: ''
+  }
+])
+const SeaPortoptions = ref([])
+const SeaContentList = ref([
+  {
+    Priority: 'P1',
+    RuleType: 'Default Rule',
+    AirPort: 'All',
+    FromDate: '',
+    ToDate: ''
+  }
+])
+
+// 选择checkbox
+const CheckChange = (val: any) => {
+  if(val.includes('Air')) {
+    isAir.value = true
+    if(AirContentList.value.length == 0) {
+      AirContentList.value.push({
+        Priority: 'P1',
+        RuleType: 'Default Rule',
+        AirPort: 'All',
+        FromDate: '',
+        ToDate: ''
+      })
+    }
+  } else {
+    isAir.value = false
+  }
+  if(val.includes('Sea')) {
+    isSea.value = true
+    if(SeaContentList.value.length == 0) {
+      SeaContentList.value.push({
+        Priority: 'P1',
+        RuleType: 'Default Rule',
+        AirPort: 'All',
+        FromDate: '',
+        ToDate: ''
+      })
+    }
+  } else {
+    isSea.value = false
+  }
+}
+
+const handleCheckboxClick = (event:any) => {
+  // 判断点击的是否为复选框输入区域
+  const isCheckboxInput = event.target.closest('.el-checkbox__inner')
+  const isCheckboxTitle = event.target.closest('.titlecheckbox')
+  if (!isCheckboxInput) {
+    // 阻止默认切换行为
+    if(!isCheckboxTitle) {
+      event.preventDefault()
+    }
+  }
+}
+
+// 选择booking window
+const ChangeFrequency = (val: any) => {
+  if(val == 1) {
+    isRecommendETA.value = false
+  } else if(val == 2) {
+    isRecommendETA.value = true
+  } else {
+    isRecommendETA.value = false
+  }
+}
+const handleInput = (val, index, type) => {
+  // 移除非数字字符
+  const numStr = val.replace(/[^\d]/g, '');
+  // 转换为整数,处理NaN情况
+  let num = parseInt(numStr, 10) || 0;
+  // 确保最小值为1
+  num = num < 1 ? 1 : num;
+  // 更新对应数据
+  AirContentList.value[index][type] = num;
+};
+
+// 删除数据
+const handleDeleteAir = (val: any) => {
+  AirContentList.value.splice(val, 1);
+  if(AirContentList.value.length == 0) {
+    isAir.value = false
+    RecommendCheckedList.value = RecommendCheckedList.value.splice(RecommendCheckedList.value.indexOf('Air'), 1)
+  }
+};
+
+// 删除数据
+const handleDeleteSea = (val: any) => {
+  SeaContentList.value.splice(val, 1);
+  if(SeaContentList.value.length == 0) {
+    isAir.value = false
+    RecommendCheckedList.value = RecommendCheckedList.value.splice(RecommendCheckedList.value.indexOf('Sea'), 1)
+  }
+};
+
+// 添加数据
+const AddAirCoulumnList = (val: any) => {
+  val.unshift({
+    Priority: 'P1',
+    RuleType: 'Default Rule',
+    AirPort: 'All',
+    FromDate: '',
+    ToDate: ''
+  })
+}
+
+// 根据RuleType的值来修改Priority的值
+const updatePriorities = () => {
+  const length = AirContentList.value.length;
+  const length2 = SeaContentList.value.length;
+  if (length === 1) {
+    handleLengthOne(AirContentList.value);
+  } else if (length === 2) {
+    handleLengthTwo(AirContentList.value);
+  } else if (length >= 3) {
+    handleLengthThreePlus(AirContentList.value);
+  }
+  if (length2 === 1) {
+    handleLengthOne(SeaContentList.value);
+  } else if (length2 === 2) {
+    handleLengthTwo(SeaContentList.value);
+  } else if (length2 >= 3) {
+    handleLengthThreePlus(SeaContentList.value);
+  }
+};
+
+// 处理长度为1
+const handleLengthOne = (item: any) => {
+  item.forEach(item => item.Priority = 'P1');
+  item.AirPort = item.RuleType === 'Default Rule' ? 'All' : '';
+};
+
+// 处理长度为2(新增核心逻辑)
+const handleLengthTwo = (item: any) => {
+  const types = new Set(item.map(i => i.RuleType));
+  item.forEach(item => {
+    item.AirPort = item.RuleType === 'Default Rule' ? 'All' : '';
+  });
+
+  // 两个都是 Default Rule
+  if (types.size === 1 && types.has('Default Rule')) {
+    item.forEach(item => item.Priority = 'P1');
+    return;
+  }
+
+  // 包含 Default Rule 和其他类型
+  if (types.has('Default Rule')) {
+    item.forEach(item => {
+      item.Priority = item.RuleType === 'Default Rule' ? 'P2' : 'P1';
+    });
+    return;
+  }
+
+  //同时存在 Specific Rule 和 Single Dimension
+  if (types.has('Specific Rule') && types.has('Single Dimension')) {
+    item.forEach(item => {
+      item.Priority = item.RuleType === 'Specific Rule' ? 'P1' : 'P2';
+    });
+    return;
+  }
+
+  // 情况4:两个相同类型(都是 Specific 或都是 Single)
+  item.forEach(item => item.Priority = 'P1');
+};
+
+// 处理长度≥3(保持之前的逻辑不变)
+const handleLengthThreePlus = (item : any) => {
+  item.forEach(item => {
+    item.AirPort = item.RuleType === 'Default Rule' ? 'All' : '';
+  });
+  // 统计各类型数量
+  const counts = item.reduce((acc, cur) => {
+    acc[cur.RuleType] = (acc[cur.RuleType] || 0) + 1;
+    return acc;
+  }, {});
+
+  // 获取所有存在的类型
+  const existingTypes = Object.keys(counts);
+  
+  // 三个不同类型都存在
+  if (
+    existingTypes.includes('Specific Rule') &&
+    existingTypes.includes('Single Dimension') &&
+    existingTypes.includes('Default Rule')
+  ) {
+    const priorityMap = {
+      'Specific Rule': 'P1',
+      'Single Dimension': 'P2',
+      'Default Rule': 'P3'
+    };
+    item.forEach(item => {
+      item.Priority = priorityMap[item.RuleType];
+    });
+    return;
+  }
+
+  // 全为同一种类型的情况
+  if (existingTypes.length === 1) {
+    item.forEach(item => item.Priority = 'P1');
+    return; // 提前退出后续判断
+  }
+
+  // 处理 Specific + Default 组合
+  if (existingTypes.length === 2 && 
+      existingTypes.includes('Specific Rule') && 
+      existingTypes.includes('Default Rule')) {
+    item.forEach(item => {
+      item.Priority = item.RuleType === 'Specific Rule' ? 'P1' : 'P2';
+    });
+    return;
+  }
+
+  // 存在两个Default Rule
+  if (counts['Default Rule'] === 2 && existingTypes.length === 2) {
+    item.forEach(item => {
+      item.Priority = item.RuleType === 'Default Rule' ? 'P2' : 'P1';
+    });
+    return;
+  }
+
+  // 存在两个Single Dimension
+  if (counts['Single Dimension'] === 2) {
+    if (existingTypes.includes('Default Rule')) {
+      // 两个Single + 一个Default
+      item.forEach(item => {
+        item.Priority = item.RuleType === 'Default Rule' ? 'P2' : 'P1';
+      });
+    } else if (existingTypes.includes('Specific Rule')) {
+      // 两个Single + 一个Specific
+      item.forEach(item => {
+        item.Priority = item.RuleType === 'Specific Rule' ? 'P1' : 'P2';
+      });
+    }
+    return;
+  }
+
+  // 其他未明确定义的情况保持原逻辑
+  const defaultPriorityMap = {
+    'Specific Rule': 'P1',
+    'Single Dimension': 'P2',
+    'Default Rule': 'P3'
+  };
+  item.forEach(item => {
+    item.Priority = defaultPriorityMap[item.RuleType] || 'P3';
+  });
+};
+
+// 深度监听数组变化
+watch(
+  () => [...AirContentList.value],
+  () => updatePriorities(),
+  { deep: true }
+);
+watch(
+  () => [...SeaContentList.value],
+  () => updatePriorities(),
+  { deep: true }
+);
+
+
+</script>
+<template>
+  <div>
+    <el-radio-group v-model="Recommendradio" @change="ChangeFrequency">
+      <el-radio :value="1">No Specific recommended time for choosing delivery date</el-radio>
+      <el-radio :value="2">
+        <div>Recommend Delivery Date (ETA/ATA )</div>
+        <div v-if="isRecommendETA" class="oceanCheckbox">
+          <el-checkbox-group @change="CheckChange" v-model="RecommendCheckedList">
+            <el-checkbox class="delayedType" value="Air" @click="handleCheckboxClick($event)" >
+              <div class="titlecheckbox">
+                <div>Air</div>
+                <span v-if="isAir" class="icon_grey font_family icon-icon_dropdown_b"></span>
+                <span v-else class="icon_grey font_family icon-icon_up_b"></span>
+              </div>
+              <div v-if="isAir" class="radiocheckbox" style="margin-top: 16px">
+                <div class="AirCoulumn">
+                  <div class="AicoulumnTitile" style="width: 10%;">Priority</div>
+                  <div class="AicoulumnTitile" style="width: 20%;">Rule Type</div>
+                  <div class="AicoulumnTitile" style="width: 40%;">Air Port</div>
+                  <div style="display: flex;flex-direction: column;border-right: 1px solid var(--color-system-border);width: 20%;">
+                    <div class="AicoulumnTitile2">Recommended Delivery Date</div>
+                    <div style="display: flex;height: 24px;align-items: center;">
+                      <div class="datetitle" style="border-right: 1px solid var(--color-system-border);">From (ETA/ATA + Days)</div>
+                      <div class="datetitle">To (ETA/ATA + Days)</div>
+                    </div>
+                  </div>
+                  <div class="AirCoumlulnAdd" style="width: 10%;" @click="AddAirCoulumnList(AirContentList)">+ Add</div>
+                </div>
+                <div class="AirContent" v-for="(item,index) in AirContentList" :key="index">
+                  <div class="AirCoumlumn" style="width: 10%;">{{item.Priority}}</div>
+                  <div class="AirCoumlumn" style="width: 20%;">
+                    <el-select
+                      v-model="item.RuleType"
+                      style="width: 100%;"
+                    >
+                      <el-option
+                        v-for="item in RuleTypeoptions"
+                        :key="item.value"
+                        :label="item.label"
+                        :value="item.value"
+                      >
+                      </el-option>
+                    </el-select>
+                  </div>
+                  <div class="AirCoumlumn" style="width: 40%;">
+                    <el-select
+                      v-model="item.AirPort"
+                      style="width: 100%;"
+                    >
+                      <el-option
+                        v-for="item in AirPortoptions"
+                        :key="item.value"
+                        :label="item.label"
+                        :value="item.value"
+                      >
+                      </el-option>
+                    </el-select>
+                  </div>
+                  <div class="AirCoumlumn" style="width: 10%;">
+                    <el-input @input="(val) => handleInput(val, index, 'FromDate')" placeholder="Input" v-model="item.FromDate"></el-input>
+                  </div>
+                  <div class="AirCoumlumn" style="width: 10%;">
+                    <el-input @input="(val) => handleInput(val, index, 'ToDate')"  placeholder="Input" v-model="item.ToDate"></el-input>
+                  </div>
+                  <div class="AirDelete" style="width: 10%;">
+                    <el-button @click="handleDeleteAir(item)" class="el-button--blue" style="height: 24px">
+                      <span class="font_family icon-icon_delete_b"></span>
+                    </el-button>
+                  </div>
+                </div>
+              </div>
+            </el-checkbox>
+            <el-checkbox class="delayedType" value="Sea" @click="handleCheckboxClick($event)">
+              <div class="titlecheckbox">
+                <div>Sea</div>
+                <span v-if="isAir" class="icon_grey font_family icon-icon_dropdown_b"></span>
+                <span v-else class="icon_grey font_family icon-icon_up_b"></span>
+              </div>
+              <div v-if="isSea" style="margin-top: 16px">
+                <div class="AirCoulumn">
+                  <div class="AicoulumnTitile" style="width: 10%;">Priority</div>
+                  <div class="AicoulumnTitile" style="width: 20%;">Rule Type</div>
+                  <div class="AicoulumnTitile" style="width: 40%;">Air Port</div>
+                  <div style="display: flex;flex-direction: column;border-right: 1px solid var(--color-system-border);width: 20%;">
+                    <div class="AicoulumnTitile2">Recommended Delivery Date</div>
+                    <div style="display: flex;height: 24px;align-items: center;">
+                      <div class="datetitle" style="border-right: 1px solid var(--color-system-border);">From (ETA/ATA + Days)</div>
+                      <div class="datetitle">To (ETA/ATA + Days)</div>
+                    </div>
+                  </div>
+                  <div class="AirCoumlulnAdd" style="width: 10%;" @click="AddAirCoulumnList(SeaContentList)">+ Add</div>
+                </div>
+                <div class="AirContent" v-for="(item,index) in SeaContentList" :key="index">
+                  <div class="AirCoumlumn" style="width: 10%;">{{item.Priority}}</div>
+                  <div class="AirCoumlumn" style="width: 20%;">
+                    <el-select
+                      v-model="item.RuleType"
+                      style="width: 100%;"
+                    >
+                      <el-option
+                        v-for="item in RuleTypeoptions"
+                        :key="item.value"
+                        :label="item.label"
+                        :value="item.value"
+                      >
+                      </el-option>
+                    </el-select>
+                  </div>
+                  <div class="AirCoumlumn" style="width: 40%;">
+                    <el-select
+                      v-model="item.AirPort"
+                      style="width: 100%;"
+                    >
+                      <el-option
+                        v-for="item in SeaPortoptions"
+                        :key="item.value"
+                        :label="item.label"
+                        :value="item.value"
+                      >
+                      </el-option>
+                    </el-select>
+                  </div>
+                  <div class="AirCoumlumn" style="width: 10%;">
+                    <el-input @input="(val) => handleInput(val, index, 'FromDate')" placeholder="Input" v-model="item.FromDate"></el-input>
+                  </div>
+                  <div class="AirCoumlumn" style="width: 10%;">
+                    <el-input @input="(val) => handleInput(val, index, 'ToDate')"  placeholder="Input" v-model="item.ToDate"></el-input>
+                  </div>
+                  <div class="AirDelete" style="width: 10%;">
+                    <el-button @click="handleDeleteSea(item)" class="el-button--blue" style="height: 24px">
+                      <span class="font_family icon-icon_delete_b"></span>
+                    </el-button>
+                  </div>
+                </div>
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+        </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-system-border);
+  background-color: var(--color-system-body-bg);
+  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);
+}
+:deep( .el-radio__inner) {
+  border: 1px solid var(--color-system-checkbox-border);
+}
+:deep(.el-radio__inner) {
+  margin-top: 7px;
+}
+:deep(.el-checkbox-group) {
+  display: flex;
+  flex-direction: column;
+}
+.oceanCheckbox {
+  margin-bottom: 8px;
+}
+.delayedType {
+  align-items: start;
+  height: fit-content;
+  margin-right: 5px;
+  border-radius: 6px;
+  padding: 13px;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  border-radius: 6px;
+  background-color: var(--color-personal-preference-bg);
+  margin-bottom: 4px;
+}
+:deep(.el-checkbox__label) {
+  width: 100%;
+}
+.titlecheckbox {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+}
+.icon_grey {
+  color: #b8bbbf;
+}
+.AirCoulumn {
+  display: flex;
+  border: 1px solid var(--color-system-border);
+  background-color: var(--color-personal-preference-bg);
+  border-radius: 6px 6px 0 0;
+}
+.AicoulumnTitile {
+  font-size: 14px;
+  font-weight: 700;
+  border-right: 1px solid var(--color-system-border);
+  height: 56px;
+  display: flex;
+  align-items: center;
+  padding: 0 8px;
+}
+.datetitle {
+  font-size: 12px;
+  font-weight: 400;
+  height: 24px;
+  width: 50%;
+  display: flex;
+  align-items: center;
+  padding: 0 8px;
+}
+.AicoulumnTitile2 {
+  font-weight: 700;
+  height: 32px;
+  display: flex;
+  border-bottom: 1px solid var(--color-system-border);
+  align-items: center;
+  padding: 0 8px;
+}
+.AirCoumlulnAdd {
+  font-size: 14px;
+  font-weight: 400;
+  height: 56px;
+  color: var(--color-theme);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0 8px;
+}
+.AirContent {
+  display: flex;
+  border: 1px solid var(--color-system-border);
+  background-color: var(--color-mode);
+  border-top: none;
+  height: 40px;
+  align-items: center;
+}
+.AirContent:last-child {
+  border-radius: 0 0 6px 6px;
+}
+.AirCoumlumn {
+  border-right: 1px solid var(--color-system-border);
+  display: flex;
+  height: 40px;
+  align-items: center;
+  padding: 0 8px;
+}
+.AirDelete {
+  display: flex;
+  height: 40px;
+  align-items: center;
+  justify-content: center;
+}
+</style>

+ 121 - 0
src/views/DesDelivery/src/components/SelectStation.vue

@@ -0,0 +1,121 @@
+<script setup lang="ts">
+
+const SelectCountry = ref('')
+const SelectCountryoptions = ref([
+  { value: 'China', label: 'China' },
+  { value: 'USA', label: 'USA' },
+  { value: 'Japan', label: 'Japan' },
+  { value: 'Korea', label: 'Korea' },
+  { value: 'Taiwan', label: 'Taiwan' },
+  { value: 'Hong Kong', label: 'Hong Kong' },
+  { value: 'Hong Kong2', label: 'Hong Kong2' },
+  { value: 'Hong Kong3', label: 'Hong Kong3' },
+  { value: 'Hong Kong4', label: 'Hong Kong4' },
+  { value: 'Hong Kong5', label: 'Hong Kong5' },
+  { value: 'Hong Kong6', label: 'Hong Kong6' },
+  { value: 'Hong Kong7', label: 'Hong Kong7' },
+])
+interface OceanCheckboxItem {
+  value: string
+  label: string
+}
+
+interface Props {
+  CheckboxList: OceanCheckboxItem[]
+  CheckedList: Array<''>
+}
+const props = defineProps<Props>()
+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 emits = defineEmits(['handleClickSelectCountry'])
+// Select Country
+const handleClickSelectCountry = (val: any) => {
+  emits('handleClickSelectCountry', val)
+}
+
+</script>
+
+<template>
+  <div>
+    <el-select
+      v-model="SelectCountry"
+      placeholder="Select Country"
+      style="width: 400px"
+      @change="handleClickSelectCountry"
+    >
+      <el-option
+        v-for="item in SelectCountryoptions"
+        :key="item.value"
+        :label="item.label"
+        :value="item.value"
+      />
+    </el-select>
+    <div class="station-list">
+      <div class="station-list-title">Station List</div>
+      <div class="oceanCheckbox">
+        <el-checkbox-group v-model="CheckedList" :class="{isEmpty :  CheckboxList.length === 0}">
+          <el-checkbox
+            v-for="item in CheckboxList"
+            :key="item.label"
+            :label="item.label"
+            :value="item.value"
+          >
+            {{ item.label }}
+          </el-checkbox>
+        </el-checkbox-group>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped> 
+.station-list {
+  background-color: var(--color-personal-preference-bg);
+  border-radius: 6px;
+  margin: 4px 0 0 0;
+  padding: 8px;
+}
+.station-list-title {
+  margin: 1px 0 13px 0;
+  font-size: 14px;
+  font-weight: 600;
+}
+:deep(.el-checkbox-group) {
+  border: 1px solid var(--color-system-border);
+  border-radius: 5px;
+}
+.isEmpty {
+  border: none;
+}
+:deep(.el-checkbox) {
+  width: 100%;
+  background-color: var(--color-system-body-bg);
+  border-bottom: 1px solid var(--color-system-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;
+  overflow-y: scroll;
+}
+</style>

+ 97 - 0
src/views/DesDelivery/src/components/SetBookingWindow.vue

@@ -0,0 +1,97 @@
+<script setup lang="ts">
+const windowradio = ref(0)
+const isBookingETD = ref(false)
+const isBookingETA = ref(false)
+const beforeETDdays = ref('')
+const afterETDdays = ref('')
+const beforeETAdays = ref('')
+const afterETAdays = ref('')
+
+// 选择booking window
+const ChangeFrequency = (val: any) => {
+  if(val == 1) {
+    isBookingETD.value = false
+    isBookingETA.value = false
+    beforeETDdays.value = ''
+    afterETDdays.value = ''
+    beforeETAdays.value = ''
+    afterETAdays.value = ''
+  } else if(val == 2) {
+    isBookingETD.value = true
+    isBookingETA.value = false
+    beforeETAdays.value = ''
+    afterETAdays.value = ''
+  } else if(val == 3) {
+    isBookingETD.value = false
+    isBookingETA.value = true
+    beforeETDdays.value = ''
+    afterETDdays.value = ''
+  } else {
+    isBookingETD.value = false
+    isBookingETA.value = false
+    beforeETDdays.value = ''
+    afterETDdays.value = ''
+    beforeETAdays.value = ''
+    afterETAdays.value = ''
+  }
+}
+
+</script>
+<template>
+  <div>
+    <el-radio-group v-model="windowradio" @change="ChangeFrequency">
+      <el-radio :value="1">No Specific time restrictions for creating booking</el-radio>
+      <el-radio :value="2">
+        <div>Booking Window (ETD)</div>
+        <div class="ETDWindow" v-if="isBookingETD">
+          <el-input style="width: 80px;height: 32px;" v-model="beforeETDdays"></el-input>
+          days before to 
+          <el-input style="width: 80px;height: 32px;" v-model="afterETDdays"></el-input>
+          days after
+        </div>
+      </el-radio>
+      <el-radio :value="3">
+        <div>Booking Window (ETA)</div>
+        <div class="ETDWindow" v-if="isBookingETA">
+          <el-input style="width: 80px;height: 32px;" v-model="beforeETAdays"></el-input>
+          days before to 
+          <el-input style="width: 80px;height: 32px;" v-model="afterETAdays"></el-input>
+          days after
+        </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-system-border);
+  background-color: var(--color-system-body-bg);
+  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);
+}
+:deep( .el-radio__inner) {
+  border: 1px solid var(--color-system-checkbox-border);
+}
+:deep(.el-radio__inner) {
+  margin-top: 7px;
+}
+.ETDWindow {
+  margin: 4px 0 9px 0;
+  font-size: 14px;
+  font-weight: 400;
+}
+</style>