Преглед на файлове

feat:添加create new booking接口

AmandaG преди 4 месеца
родител
ревизия
73e8741e14
променени са 15 файла, в които са добавени 882 реда и са изтрити 317 реда
  1. 98 0
      src/api/module/Delivery.ts
  2. 14 3
      src/stores/modules/breadCrumb.ts
  3. 39 8
      src/views/DestinationDelivery/src/components/ConfiguRations/src/ConfiguRations.vue
  4. 90 59
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/ConfigurationsTable.vue
  5. 137 52
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue
  6. 113 32
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/RecommendDate.vue
  7. 15 3
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectStation.vue
  8. 21 7
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue
  9. 20 13
      src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SetBookingWindow.vue
  10. BIN
      src/views/DestinationDelivery/src/components/ConfiguRations/src/images/default_configuration@2x.png
  11. 252 72
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue
  12. 71 65
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/components/NewbookingTable.vue
  13. BIN
      src/views/DestinationDelivery/src/components/CreateNewBooking/src/images/icon_success_big@2x.png
  14. 12 1
      src/views/DestinationDelivery/src/components/TableView/src/TableView.vue
  15. 0 2
      src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

+ 98 - 0
src/api/module/Delivery.ts

@@ -100,4 +100,102 @@ export const getConfigurationList = (params: any, config: any) => {
     },
     config
   )
+}
+/**
+ * delete Configuration table list
+ */
+export const deleteConfigurationList = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_config',
+      operate: 'delete',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * Create New Rule页面初始化
+ */
+export const InitCreateRule = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_config',
+      operate: 'add',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * Create New Booking页面初始化
+ */
+export const InitCreateBooking = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_booking',
+      operate: 'add',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * Create New Booking表格column
+ */
+export const BookingTableColumn = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_booking',
+      operate: 'destination_delivery_shipment_search',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * Create New Booking查询表格data
+ */
+export const BookingTableSearch = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_booking',
+      operate: 'search_shipment',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 根据选择的list获取address book
+ */
+export const getAddressBookList = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_load',
+      operate: 'manage_address',
+      ...params
+    },
+    config
+  )
+}
+/**
+ * 保存booking
+ */
+export const SaveBookingList = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_booking',
+      operate: 'save',
+      ...params
+    },
+    config
+  )
 }

+ 14 - 3
src/stores/modules/breadCrumb.ts

@@ -99,6 +99,12 @@ export const useBreadCrumb = defineStore('breadCrumb', {
           }
         ]
       } else if (toRoute.name === 'Destination Create New Rule') {
+        let label = ''
+        if(toRoute.query.a != undefined) {
+          label = 'Modify Rule'
+        } else {
+          label = 'Create New Rule'
+        }
         this.routeList = [
           {
             label: 'Destination Delivery',
@@ -111,12 +117,17 @@ export const useBreadCrumb = defineStore('breadCrumb', {
             query: ''
           },
           {
-            label: 'Create New Rule',
+            label: label,
             path: '/destination-delivery/CreateNewRule',
             query: toRoute.query
           }
         ]
-      } else if (toRoute.name === 'Create New Booking') {
+      } else if (toRoute.name === 'Create New Booking') {let label = ''
+        if(toRoute.query.a != undefined) {
+          label = 'Modify Booking'
+        } else {
+          label = 'Create New Booking'
+        }
         this.routeList = [
           {
             label: 'Destination Delivery',
@@ -124,7 +135,7 @@ export const useBreadCrumb = defineStore('breadCrumb', {
             query: ''
           },
           {
-            label: 'Create New Booking',
+            label: label,
             path: '/destination-delivery/CreateNewBooking',
             query: toRoute.query
           }

+ 39 - 8
src/views/DestinationDelivery/src/components/ConfiguRations/src/ConfiguRations.vue

@@ -1,44 +1,69 @@
 <script setup lang="ts">
 import ConfigurationsTable from './components/ConfigurationsTable.vue'
 import { useCalculatingHeight } from '@/hooks/calculatingHeight'
+import { useRouter } from 'vue-router'
 
 const filterRef: Ref<HTMLElement | null> = ref(null)
+  const router = useRouter()
 
 const AddRulesTableColumns = ref([
   {
-    field: 'Country',
+    field: 'country',
     title: 'Country',
-    type: 'normal',
+    type: 'link',
+    width: '10%',
     formatter: ''
   },
   {
-    field: 'Stations',
+    field: 'station',
     title: 'Stations',
     type: 'normal',
+    width: '20%',
     formatter: ''
   },{
-    field: 'Booking Window',
+    field: 'booking_window_desc',
     title: 'Booking Window',
     type: 'normal',
     formatter: ''
   },
   {
-    field: 'Reecommended Delivery Date',
+    field: 'recommended_delivery_date_desc',
     title: 'Reecommended Delivery Date',
     type: 'normal',
     formatter: ''
   }
 ])
-const containerHeight = useCalculatingHeight(document.documentElement, 206, [filterRef])
+const containerHeight = useCalculatingHeight(document.documentElement, 290, [filterRef])
+
+const tabledatalength = ref()
+const gettabledatalength = (val: any) => {
+  tabledatalength.value = val
+}
 
+// 跳转Create New Rule页面
+const ToCreateRule = () => {
+  router.push({
+    path: '/destination-delivery/ConfiguRations/CreateNewRule',
+    query: {}
+  })
+}
 </script>
 
 <template>
   <div>
     <div class="Title">Configuration</div>
     <div class="AddRules">
-      Added Rules
-      <ConfigurationsTable :height="containerHeight" :ColumnsList="AddRulesTableColumns"></ConfigurationsTable>
+      <div class="monitoring_flex">
+        <div class="subscribedTitle">Added Rules</div>
+        <el-button
+          class="el-button--main"
+          style="height: 40px"
+          v-if="tabledatalength != 0 && tabledatalength != null"
+          @click="ToCreateRule"
+          >+ Add Rule</el-button
+        >
+      </div>
+      <ConfigurationsTable :height="containerHeight" :ColumnsList="AddRulesTableColumns" @gettabledatalength="gettabledatalength"></ConfigurationsTable>
     </div>
   </div>
 </template>
@@ -61,4 +86,10 @@ const containerHeight = useCalculatingHeight(document.documentElement, 206, [fil
   font-weight: 700;
   padding: 37px 0 13px 24px;
 }
+.monitoring_flex {
+  display: flex;
+  justify-content: space-between;
+  padding-right: 24px;
+  align-items: end;
+}
 </style>

+ 90 - 59
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/ConfigurationsTable.vue

@@ -1,10 +1,11 @@
 <script setup lang="ts">
 import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
 import { useRowClickStyle } from '@/hooks/rowClickStyle'
-import { formatTimezone } from '@/utils/tools'
+import { formatTimezone, formatNumber } from '@/utils/tools'
 import { ref, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
 import { useVisitedRowState } from '@/stores/modules/visitedRow'
+import DefaultConfiguration from '../images/default_configuration@2x.png'
 
 const visitedRowState = useVisitedRowState()
 const router = useRouter()
@@ -35,27 +36,24 @@ const tableData = ref<VxeGridProps<any>>({
     backgroundColor: 'var(--color-table-header-bg)'
   },
   columnConfig: { resizable: true, useKey: true },
-  rowConfig: { isHover: true, isCurrent: true },
+  rowConfig: { isHover: true }
 })
 
 const tableRef = ref<VxeGridInstance | null>(null)
+const pageInfo = ref({ pageNo: 1, pageSize: 15, total: 0 })
 
 const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
       title: item.title,
-      field: item.field
+      field: item.field,
+      width: item.width
     }
     // 格式化
-    if (item.formatter === 'date') {
+    if (item.type === 'link') {
       curColumn = {
         ...curColumn,
-        formatter: ({ cellValue }: any) => formatTimezone(cellValue)
-      }
-    }  else if (item.type === 'link') {
-      curColumn = {
-        ...curColumn,
-        slots: { default: 'trackingNo' }
+        slots: { default: 'countryNo' }
       }
     }
     return curColumn
@@ -73,13 +71,21 @@ const getTableColumns = async () => {
   })
 }
 // 获取表格数据
-const getTableData = () => {
+const getTableData = (isPageChange?: boolean) => {
+  const rc = isPageChange ? pageInfo.value.total : -1
   $api
     .getConfigurationList({
+      cp: pageInfo.value.pageNo,
+      ps: pageInfo.value.pageSize,
+      rc
     })
     .then((res: any) => {
       if (res.code === 200) {
+        pageInfo.value.total = Number(res.data.rc)
+        pageInfo.value.pageNo = res.data.cp
+        pageInfo.value.pageSize = res.data.ps
         tableData.value.data = res.data.searchData
+        emits('gettabledatalength', res.data.searchData.length)
       }
     })
 }
@@ -87,36 +93,40 @@ const getTableData = () => {
 // 实现行点击样式
 useRowClickStyle(tableRef)
 
-const emits = defineEmits(['deleteAddedRules'])
+const emits = defineEmits(['gettabledatalength'])
 // 点击删除
 const handleDelete = (row: any) => {
+  row.visible = true
+}
+
+const deleteMoniTable = (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)
-      }
-    })
+  .deleteConfigurationList({
+    a: row._serial_no
+  })
+  .then((res: any) => {
+    if (res.code === 200) {
+      tableData.value.data = tableData.value.data?.filter((item) => item._serial_no !== row._serial_no)
+      emits('gettabledatalength', tableData.value.data?.length)
+    }
+  })
 }
 
-// 点击link字段
-const handleLinkClick = (row: any) => {
+// 编辑表格数据
+const handleedit = ({ row }: any) => {
   router.push({
-    path: '/shipment/detail',
-    query: { a: row.__serial_no, _schemas: row._schemas }
+    path: '/destination-delivery/ConfiguRations/CreateNewRule',
+    query: { a: row._serial_no}
   })
-  sessionStorage.setItem('activeTab', 'Subscribe Notifications')
-  visitedRowState.setTrackingTableData(row['__serial_no'])
 }
 
-onMounted(() => {
-  getTableColumns()
-  getTableData()
-})
+const handleedittow = (row: any) => {
+  router.push({
+    path: '/destination-delivery/ConfiguRations/CreateNewRule',
+    query: {a: row._serial_no}
+  })
+}
 
 const clickAddNewRule = () => {
   router.push({
@@ -124,29 +134,26 @@ const clickAddNewRule = () => {
     query: {}
   })
 }
-const getpaginationTableData = () => {
-  $api
-    .getConfigurationList({
-    })
-    .then((res: any) => {
-      if (res.code === 200) {
-        console.log(res.data)
-        tableData.value.data = res.data.tableData
-      }
-    })
-}
+
+onMounted(() => {
+  getTableColumns()
+  getTableData()
+})
 </script>
 
 <template>
   <div class="SettingTable">
     <vxe-grid
       ref="tableRef"
-      :style="{ border: 'none' }"
-      :height="props.height"
+      :style="{ border: 'none'}"
       v-bind="tableData"
+      :height="props.height"
+      @cell-dblclick="handleedit"
     >
+      <!-- 空数据时的插槽 -->
       <template #empty>
         <div>
+          <img :src="DefaultConfiguration" width="100" />
           <div class="empty-text">
             Configure available destination delivery regions and time slots.
           </div>
@@ -154,16 +161,15 @@ const getpaginationTableData = () => {
         </div>
       </template>
       <!-- Tracking No字段的插槽 -->
-      <template #trackingNo="{ row, column }">
+      <template #countryNo="{ row, column }">
         <span
-          style="color: var(--color-theme); cursor: pointer"
-          @click="handleLinkClick(row)"
+          style="color: var(--color-theme)"
         >
           {{ row[column.field] }}
         </span>
       </template>
       <template #action="{ row }">
-        <el-button class="el-button--blue" style="height: 24px">
+        <el-button class="el-button--blue" style="height: 24px" @click="handleedittow(row)">
           <span class="font_family icon-icon_edit_b"></span>
         </el-button>
         <el-popover trigger="click" :visible="row.visible" placement="left" :width="480">
@@ -176,7 +182,7 @@ const getpaginationTableData = () => {
             <el-button style="width: 100px" class="el-button--default" @click="row.visible = false"
               >cancel</el-button
             >
-            <el-button style="width: 100px" type="warning">
+            <el-button style="width: 100px" type="warning" @click="deleteMoniTable(row)">
               OK
             </el-button>
           </div>
@@ -189,13 +195,27 @@ const getpaginationTableData = () => {
       </template>
     </vxe-grid>
   </div>
+  <div class="pagination">
+    <span>Total {{ formatNumber(pageInfo.total) }}</span>
+    <el-pagination
+      v-model:current-page="pageInfo.pageNo"
+      v-model:page-size="pageInfo.pageSize"
+      layout="prev, pager, next"
+      :total="pageInfo.total"
+      :pager-count="5"
+      @size-change="getTableData(true)"
+      @current-change="getTableData(true)"
+      background
+    />
+  </div>
 </template>
 
 <style lang="scss" scoped>
 .SettingTable {
-  padding: 13px 24px 0 0;
+  margin: 13px 24px 0 0;
+  font-weight: 400;
 }
-.font_family::before {
+.icon-icon_delete_b::before {
   color: var(--color-btn-danger-bg);
 }
 .icon_alert::before {
@@ -213,16 +233,27 @@ const getpaginationTableData = () => {
   color: var(--color-neutral-1);
   padding: 15px 0 33px 37px;
 }
+.empty-text {
+  margin: 8px 0;
+  color: var(--color-neutral-2);
+}
+.pagination {
+  display: flex;
+  justify-content: space-between;
+  font-weight: 400;
+  font-size: 15px;
+  align-items: center;
+  margin-right: 24px;
+  border: 1px solid var(--color-border);
+  border-top: none;
+  padding: 4px 8px;
+  border-radius: 0 0 6px 6px;
+}
 :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;
+
+:deep(.vxe-table--empty-placeholder) {
+  height: 500px !important;
 }
 </style>

+ 137 - 52
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/CreateNewRule.vue

@@ -6,11 +6,17 @@ import { useRouter } from 'vue-router'
 import submitsucessful from '../images/icon_success_big@2x.png'
 
 const router = useRouter()
+const { currentRoute } = router
+const { value } = currentRoute
+const { query } = value
+const { a } = query
 
 const activeRules = ref(['SelectStation', 'SelectBooking', 'KLNPLC', 'RecommendDeliveryDate'])
 const IsFirstActive = ref(true)
 const selectedCountry = ref('')
 const windowRadio = ref()
+const setbookingdata = ref({})
+const recommendata = ref({})
 const recommendRadio = ref()
 const windowBeforeDays = ref('')
 const windowAfterDays = ref('')
@@ -22,6 +28,8 @@ const IsFourActive = ref(true)
 const IsThreeActive = ref(true)
 const CancelRulesVisible = ref(false)
 const SaveedVisible = ref(false)
+const UnableSaveVisible = ref(false)
+const missingmessage = ref('')
 const KLNPLCvalue = ref('')
 interface KLNItem {
   value: string
@@ -32,6 +40,38 @@ const KLNLists = ref<KLNItem[]>([])
 const countryCheckedList = ref([])
 const CountryCheckboxList = ref([])
 
+// 页面初始化
+const InitRuleData = () => {
+  if ( a!= undefined ) {
+    $api
+    .InitCreateRule({
+      a: a
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        const { returnData } = res.data
+        KLNPLCvalue.value = returnData.kln_pic
+        selectedCountry.value = returnData.country
+        setbookingdata.value = returnData.SetBookingWindow
+        windowRadio.value = returnData.SetBookingWindow.windowradio
+        if(windowRadio.value != 1) {
+          windowBeforeDays.value = returnData.booking_window_date_start
+          windowAfterDays.value = returnData.booking_window_date_end
+        }
+        recommendRadio.value = returnData.RcommendDeliveryDate.Recommendradio
+        if(recommendRadio.value != 1) {
+          recommendCheckedList.value = returnData.RcommendDeliveryDate.RecommendCheckedList
+          recommendCheckedAirList.value = returnData.RcommendDeliveryDate.RecommendCheckedAirList
+          recommendCheckedSeaList.value = returnData.RcommendDeliveryDate.RecommendCheckedSeaList
+        }
+        countryCheckedList.value = returnData.station
+        CountryCheckboxList.value = returnData.CountryCheckedList
+        recommendata.value = returnData.RcommendDeliveryDate
+      }
+    })
+  }
+}
+
 
 const CreateRuleDisabled = computed(() => {
   // 1. 检查基本条件是否满足
@@ -40,7 +80,8 @@ const CreateRuleDisabled = computed(() => {
     selectedCountry.value === '' ||
     windowRadio.value === undefined ||
     recommendRadio.value === undefined ||
-    KLNPLCvalue.value === ''
+    KLNPLCvalue.value === '' || 
+    KLNPLCvalue.value === '无搜索结果'
   ) {
     return true;
   }
@@ -51,11 +92,6 @@ const CreateRuleDisabled = computed(() => {
     if (windowBeforeDays.value === '' || windowAfterDays.value === '') {
       return true;
     }
-  } else {
-    // 当 windowRadio 为 1 时,时间窗口字段应为空
-    if (windowBeforeDays.value !== '' || windowAfterDays.value !== '') {
-      return true;
-    }
   }
 
   // 3. 处理推荐日期条件
@@ -106,22 +142,10 @@ const handleChangeStation = (val:any) =>{
 // select booking window
 const bookingWindow = ref('')
 const bookingdetail = ref('')
-const changeBookingWindow = (radio: number, beforedays:any, afterdays:any, details: any) => {
+const changeBookingWindow = (radio: number, beforedays:any, afterdays:any) => {
   windowRadio.value = radio
-  bookingdetail.value = details
-  if(radio == 1) {
-    bookingWindow.value = 'No_Restrictions'
-    windowBeforeDays.value = ''
-    windowAfterDays.value = ''
-  } else if(radio == 2) {
-    bookingWindow.value = 'Restrictions_ETD_ATD'
-    windowBeforeDays.value = beforedays
-    windowAfterDays.value = afterdays
-  } else if(radio == 3) {
-    bookingWindow.value = 'Restrictions_ETA_ATA'
-    windowBeforeDays.value = beforedays
-    windowAfterDays.value = afterdays
-  }
+  windowBeforeDays.value = beforedays
+  windowAfterDays.value = afterdays
 }
 
 // select recommend date
@@ -133,18 +157,6 @@ const checkRecommend = (checked: any, airlist: any, sealist: any, radio: number)
   recommendCheckedAirList.value = airlist
   recommendCheckedSeaList.value = sealist
   recommendRadio.value = radio
-  if(radio == 1) {
-    recommendDelivery.value = 'No_Recommended'
-    recommenddetail.value = 'No Specific recommended time for choosing delivery date'
-  } else {
-    recommendDelivery.value = 'Delivery_ETA_ATA'
-    if(checked.includes('Air')) {
-      recommenddetail.value += 'Air:\nDefault Rule- Air Port: All,\nRecommend Delivery Date: ETA/ATA+' + airlist[0].recommended_delivery_from + ' Days to ETA/ATA+'+ airlist[0].recommended_delivery_to + ' Days;' 
-    }
-    if(checked.includes('Sea')) {
-      recommenddetail.value += 'Sea:\nDefault Rule- ort: All, Carrier: All,\nRecommend Delivery Date: ETA/ATA+' + sealist[0].recommended_delivery_from + ' Days to ETA/ATA+'+ sealist[0].recommended_delivery_to + ' Days;' 
-    }
-  }
 }
 
 // 获取KLN列表
@@ -184,6 +196,7 @@ const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
     }
   }, 1000 * Math.random())
 }
+
 const createFilter = (queryString: string) => {
   return (restaurant: KLNItem) => {
     return (
@@ -191,13 +204,6 @@ const createFilter = (queryString: string) => {
     )
   }
 }
-const handleSelectKLN = (item: Record<string, any>) => {
-  console.log(item)
-}
-
-onMounted(() => {
-  getKLNList()
-})
 
 // 保存
 const handleSubmitRule = () => {
@@ -217,7 +223,49 @@ const handleSubmitRule = () => {
       return []
     }
   })
-  $api.handelSaveRule({ 
+  let airData = []
+  let airlistInfo = {}
+  let mergeData = []
+  if(windowRadio.value == 1) {
+    bookingWindow.value = 'No_Restrictions'
+    bookingdetail.value = 'No Specific time restrictions for creating booking'
+  } else if(windowRadio.value == 2) {
+    bookingWindow.value = 'Restrictions_ETD_ATD'
+    bookingdetail.value = 'ETD/ATD: ' + windowBeforeDays.value + ' days before to ' + windowAfterDays.value + ' days after'
+  } else if(windowRadio.value == 3) {
+    bookingWindow.value = 'Restrictions_ETA_ATA'
+    bookingdetail.value = 'ETA/ATA: ' + windowBeforeDays.value + ' days before to ' + windowAfterDays.value + ' days after'
+  }
+  if(recommendRadio.value == 1) {
+    recommendDelivery.value = 'No_Recommended'
+    recommenddetail.value = 'No Specific recommended time for choosing delivery date'
+  } else {
+    recommendDelivery.value = 'Delivery_ETA_ATA'
+    if(recommendCheckedList.value.includes('Air')) {
+      recommenddetail.value += 'Air:\nDefault Rule- Air Port: ALL,\nRecommend Delivery Date: ETA/ATA+' + airlist[0].recommended_delivery_from + ' Days to ETA/ATA+'+ airlist[0].recommended_delivery_to + ' Days;\n'
+      airlist.forEach((item) => {
+        item.ports = item.ports.join(',')
+        item.carrier = ''
+      })
+      mergeData = [...airlist]
+    }
+    if(recommendCheckedList.value.includes('Sea')) {
+      recommenddetail.value += 'Sea:\nDefault Rule- ort: ALL, Carrier: ALL,\nRecommend Delivery Date: ETA/ATA+' + seaList[0].recommended_delivery_from + ' Days to ETA/ATA+'+ seaList[0].recommended_delivery_to + ' Days;' 
+      seaList.forEach((item) => {
+        item.ports = item.ports.join(',')
+        item.carrier = item.carrier.join(',')
+      })
+      mergeData = [...mergeData , ...seaList]
+    }
+  }
+  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,
     station: countryCheckedList.value,
     booking_window: bookingWindow.value,
@@ -225,25 +273,35 @@ const handleSubmitRule = () => {
     booking_window_date_end: windowAfterDays.value,
     recommended_delivery: recommendDelivery.value,
     booking_window_desc: bookingdetail.value,
+    kln_pic: KLNPLCvalue.value,
     recommended_delivery_date_desc: recommenddetail.value,
-    ...airlist,
-    ...seaList
+    ...airlistInfo
   }).then((res: any) => {
-    if (res.code === 200) {
+    if (res.code === 200 && res.data.msg == 'success') {
       SaveedVisible.value = true
       setTimeout(() => {
         SaveedVisible.value = false
         router.push({ name: 'Configurations'})
       }, 3000)
+    } else {
+        UnableSaveVisible.value = true
+        missingmessage.value = res.data.msg
     }
   })
 }
+
+onMounted(() => {
+  getKLNList()
+  InitRuleData()
+})
+
 </script>
 
 <template>
   <div>
     <div class="Title">
-      Create New Rule
+      <div v-if="a != undefined">Modify Rule</div>
+      <div v-else>Create New Rule</div>
       <div class="operator">
         <el-button @click="CancelRulesVisible = true" style="height: 40px; width: 115px" type="default">
           <span style="margin-right: 4px" class="font_family icon-icon_return_b"></span>
@@ -287,8 +345,8 @@ const handleSubmitRule = () => {
           </template>
         </el-dialog>
         <!-- 保存失败 -->
-        <!-- <el-dialog v-model="UnableSaveVisible" width="480">
-          <div>{{ missingmessage }} missing.</div>
+        <el-dialog v-model="UnableSaveVisible" width="480">
+          <div>{{ missingmessage }}</div>
           <div>Please complete all required fields.</div>
           <template #footer>
             <div class="dialog-footer">
@@ -311,7 +369,7 @@ const handleSubmitRule = () => {
               Unable to Save
             </div>
           </template>
-        </el-dialog> -->
+        </el-dialog>
         <!-- 保存成功 -->
         <el-dialog v-model="SaveedVisible" width="320" style="height: 212px">
           <div style="text-align: center"><el-image :src="submitsucessful" style="width: 64px;" /></div>
@@ -336,10 +394,12 @@ const handleSubmitRule = () => {
               </div>
             </template>
             <div>
-              <SelectStation @handleClickSelectCountry="handleClickSelectCountry"
+              <SelectStation
+              @handleClickSelectCountry="handleClickSelectCountry"
               @handleChangeStation="handleChangeStation"
               :CheckboxList="CountryCheckboxList"
               :CheckedList="countryCheckedList"
+              :SelectCountry="selectedCountry"
               ></SelectStation>
             </div>
           </el-collapse-item>
@@ -360,7 +420,7 @@ const handleSubmitRule = () => {
             </template>
             <div>
               <SetBookingWindow
-                :windowRadio="windowRadio"
+                :setbookingdata="setbookingdata"
                 @changeBookingWindow="changeBookingWindow"
               ></SetBookingWindow>
             </div>
@@ -382,7 +442,7 @@ const handleSubmitRule = () => {
             </template>
             <div>
               <RecommendDate
-                :recommendRadio="recommendRadio"
+                :recommendata="recommendata"
                 @chackchangerecommend="checkRecommend"
               ></RecommendDate>
             </div>
@@ -408,7 +468,6 @@ const handleSubmitRule = () => {
                 style="width: 400px;margin-bottom: 5px;"
                 placeholder="Select Employee Account"
                 :fetch-suggestions="querySearchAsync"
-                @select="handleSelectKLN"
               >
                 <template #default="{ item }">
                   <div :class="[
@@ -520,4 +579,30 @@ const handleSubmitRule = () => {
 .suggestion-item {
   padding: 5px 20px;
 }
+.cancel_header {
+  font-size: 18px;
+  font-weight: 700;
+  color: var(--color-neutral-1);
+  display: flex;
+  align-items: center;
+}
+.icon_warning {
+  width: 22px;
+  height: 22px;
+  margin-right: 0;
+  fill: var(--color-btn-warning-bg);
+}
+.iconfont_warning {
+  display: flex;
+  align-items: center;
+}
+:deep(header.el-dialog__header) {
+  background-color: var(--color-system-body-bg);
+}
+:deep(footer.el-dialog__footer) {
+  border-top: none;
+}
+:deep(.el-dialog__body) {
+  font-weight: 400;
+}
 </style>

+ 113 - 32
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/RecommendDate.vue

@@ -22,31 +22,28 @@ interface RuleItem {
   carrier?: string[];
   PortList?:PortOption[];
   CarrierList?:PortOption[];
-  recommended_delivery_from: string | number;
-  recommended_delivery_to: string | number;
+  recommended_delivery_from: string;
+  recommended_delivery_to: string;
 }
 
 // 定义 RuleItem 中数组字段的类型
 type ArrayFields = 'ports' | 'carrier';
 
 const props = defineProps({
-  Recommendradio: {
-    type: Number,
-    default: 0
-  },
   recommendata: {
     type: Object,
     default: () => ({})
   }
 })
-
 // 响应式变量
-const Recommendradio = ref(props.Recommendradio)
+const Recommendradio = ref()
+const AirPortRef = ref()
+const SeaPortRef = ref()
+const SeaCarrierRef = ref()
 const isRecommendETA = ref(false)
 const isAir = ref(false)
 const isSea = ref(false)
 const RecommendCheckedList = ref<string[]>([])
-
 // 选项配置
 const AirRuleTypeoptions = ref<RuleOption[]>([
   { label: 'Specific Rule', value: 'Specific Rule' }
@@ -62,7 +59,7 @@ const AirContentList = ref<RuleItem[]>([
   {
     priority: 'P1',
     rule_type: '*Default Rule',
-    ports: ['All'],
+    ports: ['ALL'],
     recommended_delivery_from: '',
     recommended_delivery_to: '',
     mode_type: 'air',
@@ -73,8 +70,8 @@ const SeaContentList = ref<RuleItem[]>([
   {
     priority: 'P1',
     rule_type: '*Default Rule',
-    ports: ['All'],
-    carrier: ['All'],
+    ports: ['ALL'],
+    carrier: ['ALL'],
     recommended_delivery_from: '',
     recommended_delivery_to: '',
     mode_type: 'sea',
@@ -83,6 +80,32 @@ const SeaContentList = ref<RuleItem[]>([
   }
 ])
 
+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(RecommendCheckedList.value.includes('Sea')) {
+      isSea.value = true
+    }
+    AirContentList.value = recommendata.value.RecommendCheckedAirList
+    SeaContentList.value = recommendata.value.RecommendCheckedSeaList
+  } 
+  }
+}
+
+watch(() => props.recommendata, (val) => { 
+  recommendata.value = val
+  initRecommendData()
+}, { immediate: true, deep: true })
+
+
 // 创建规则项的工厂函数
 function createRuleItem(type: 'Air' | 'Sea', ruleType: string): RuleItem {
   const baseItem = {
@@ -94,15 +117,15 @@ function createRuleItem(type: 'Air' | 'Sea', ruleType: string): RuleItem {
   if (type === 'Air') {
     return {
       ...baseItem,
-      ports: ruleType === '*Default Rule' ? ['All'] : [],
+      ports: ruleType === '*Default Rule' ? ['ALL'] : [],
       mode_type: 'air',
       PortList: JSON.parse(JSON.stringify(AirPorList.value))
     }
   }
   return {
     ...baseItem,
-    ports: ruleType === '*Default Rule' ? ['All'] : [],
-    carrier: ruleType === '*Default Rule' ? ['All'] : [],
+    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))
@@ -141,12 +164,22 @@ const ChangeFrequency = (val: number) => {
 const handleInput = (val: string, index: number, type: 'recommended_delivery_from' | 'recommended_delivery_to', list: RuleItem[]) => {
   // 移除非数字字符
   const numStr = val.replace(/[^\d]/g, '');
-  // 转换为整数,处理NaN情况
-  let num = parseInt(numStr, 10) || 0;
-  // 确保最小值为1
-  num = num < 1 ? 1 : num;
-  // 更新对应数据
-  list[index][type] = num;
+  // 处理空值情况
+  if (numStr === '') {
+    list[index][type] = '';
+    return;
+  }
+  
+  // 转换为数字以进行范围检查
+  const num = parseInt(numStr, 10);
+  
+  // 确保最小值为1(但保持为字符串形式)
+  if (num < 1) {
+    list[index][type] = '1';
+  } else {
+    // 保持为字符串形式
+    list[index][type] = numStr;
+  }
 };
 // 删除数据
 const handleDelete = (index: number, list: RuleItem[], type: 'Air' | 'Sea') => {
@@ -184,10 +217,10 @@ const updateListPriorities = (list: RuleItem[], type: 'Air' | 'Sea') => {
   list.forEach(item => {
     if (item.rule_type === '*Default Rule') {
       if (type === 'Air') {
-        item.ports = ['All']
+        item.ports = ['ALL']
       } else {
-        item.ports = ['All']
-        item.carrier = ['All']
+        item.ports = ['ALL']
+        item.carrier = ['ALL']
       }
     }
   })
@@ -301,9 +334,30 @@ const handleLengthThreePlus = (list: RuleItem[], type: string) => {
 }
 // 修复:改变选项值 - 使用类型保护
 const changeSelectedValue = (val: string[], index: number, field: ArrayFields, list: RuleItem[]) => {
-  // 使用类型断言确保安全
   const item = list[index] as Record<ArrayFields, string[]>;
   item[field] = val;
+  // 新增逻辑:检查是否从 Single Dimension 变为 Specific Rule
+  if (item['rule_type'] != '*Default Rule') {
+    if (item['mode_type'] === 'air') {
+      // Air 规则:只检查 ports
+      if (item.ports && item.ports.length > 0 && !item.ports.includes('ALL')) {
+        item['rule_type'] = 'Specific Rule';
+        updatePriorities();
+      }
+    } else if (item['mode_type'] === 'sea') {
+      // Sea 规则:检查 ports 和 carrier
+      const portsSelected = item.ports && item.ports.length > 0 && !item.ports.includes('ALL');
+      const carrierSelected = item.carrier && item.carrier.length > 0 && !item.carrier.includes('ALL');
+      
+      if (portsSelected && carrierSelected) {
+        item['rule_type'] = 'Specific Rule';
+        updatePriorities();
+      } else {
+        item['rule_type'] = 'Single Dimension';
+        updatePriorities();
+      }
+    }
+  }
 }
 // 改变规则类型
 const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
@@ -315,8 +369,8 @@ const changeRuleType = (val: string, index: number, list: RuleItem[]) => {
     if ('carrier' in item) item.carrier = []
   } else if (val === '*Default Rule') {
     // 切换到默认规则时设置默认值
-    if ('ports' in item) item.ports = ['All']
-    if ('carrier' in item) item.carrier = ['All']
+    if ('ports' in item) item.ports = ['ALL']
+    if ('carrier' in item) item.carrier = ['ALL']
   }
   item.rule_type = val
   updatePriorities()
@@ -337,6 +391,13 @@ const getPortList = (type: any) => {
         // 更新现有行的列表
         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') {
@@ -344,6 +405,13 @@ const getPortList = (type: any) => {
         // 更新现有行的列表
         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);
         })
       }
     }
@@ -360,13 +428,23 @@ const getCarrierList = () => {
       // 更新现有行的列表
       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);
       })
     }
   })
 }
-getPortList('air')
-getPortList('sea')
-getCarrierList()
+
+onMounted(() => {
+  getPortList('air')
+  getPortList('sea')
+  getCarrierList()
+})
 </script>
 
 <template>
@@ -402,7 +480,7 @@ getCarrierList()
                   <div class="AirCoumlumn" style="width: 20%;">
                     <el-select
                       v-model="item.rule_type"
-                      :disabled="item.rule_type === '*Default Rule'"
+                      disabled
                       style="width: 100%;"
                       @change="val => changeRuleType(val, index, AirContentList)"
                     >
@@ -416,6 +494,7 @@ getCarrierList()
                   </div>
                   <div class="AirCoumlumn" style="width: 40%;">
                     <SelectValue
+                      ref="AirPortRef"
                       :SelectIndex="index"
                       :SelectedValue="item.ports"
                       :typeisDisabled="item.rule_type"
@@ -477,7 +556,7 @@ getCarrierList()
                   <div class="AirCoumlumn" style="width: 14%;">
                     <el-select
                       v-model="item.rule_type"
-                      :disabled="item.rule_type === '*Default Rule'"
+                      disabled
                       style="width: 100%;"
                       @change="val => changeRuleType(val, index, SeaContentList)"
                     >
@@ -491,6 +570,7 @@ getCarrierList()
                   </div>
                   <div class="AirCoumlumn" style="width: 23%;">
                     <SelectValue
+                      ref="SeaPortRef"
                       :SelectIndex="index"
                       :SelectedValue="item.ports"
                       :typeisDisabled="item.rule_type"
@@ -501,6 +581,7 @@ getCarrierList()
                   </div>
                   <div class="AirCoumlumn" style="width: 23%;">
                     <SelectValue
+                      ref="SeaCarrierRef"
                       :SelectIndex="index"
                       :SelectedValue="item.carrier"
                       :typeisDisabled="item.rule_type"

+ 15 - 3
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectStation.vue

@@ -1,8 +1,5 @@
 <script setup lang="ts">
-import { popperContentEmits } from 'element-plus'
 
-
-const SelectCountry = ref('')
 interface CountryItem {
   value: string
   label: string
@@ -17,10 +14,12 @@ interface OceanCheckboxItem {
 interface Props {
   CheckboxList: OceanCheckboxItem[]
   CheckedList: Array<''>
+  SelectCountry: string
 }
 const props = defineProps<Props>()
 const CheckedList = ref(props.CheckedList)
 const CheckboxList = ref(props.CheckboxList)
+const SelectCountry = ref(props.SelectCountry)
 watch(
   () => props.CheckboxList,
   (current) => {
@@ -33,6 +32,12 @@ watch(
     CheckedList.value = current
   }
 )
+watch(
+  () => props.SelectCountry,
+  (current) => {
+    SelectCountry.value = current
+  }
+)
 
 const emits = defineEmits(['handleClickSelectCountry','handleChangeStation'])
 const handlechangestation = (val: any) => {
@@ -95,6 +100,12 @@ const handleSelect = (item: Record<string, any>) => {
   })
 }
 
+const handleChange = (val: any) => {
+  if(val == '') {
+    CheckboxList.value = []
+  }
+}
+
 onMounted(() => {
   getCountryList()
 })
@@ -109,6 +120,7 @@ onMounted(() => {
       :popper-class="popperClass"
       :fetch-suggestions="querySearchAsync"
       @select="handleSelect"
+      @input="handleChange"
     >
       <template #default="{ item }">
         <div :class="[

+ 21 - 7
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SelectValue.vue

@@ -39,15 +39,25 @@ watch(
   () => props.SelectedValue,
   (val) => {
     value.value = val
+    if(val[0] == 'ALL') {
+      checkAll.value = true
+    }
   }
-)
+, { immediate: true, deep: true })
+
 watch(
   () => props.PortList,
   (val) => {
     PortList.value = val
   }
-)
+, { immediate: true, deep: true })
 
+const initGetPortsList = (val: any) => {
+  PortList.value = val.map(item => ({
+    ...item,
+    checked: value.value.includes(item.value)
+  }));
+}
 const emits = defineEmits(['changeSelectedValue', 'handelremovetag'])
 
 watch(value, (val) => {
@@ -55,8 +65,8 @@ watch(value, (val) => {
     checkAll.value = false
   } else if (val.length === PortList.value.length) {
     checkAll.value = true
-    value.value = ['All']
-  }else if (val.length == 1 && val[0] == 'All') {
+    value.value = ['ALL']
+  }else if (val.length == 1 && val[0] == 'ALL') {
     checkAll.value = true
   } else {
     checkAll.value = false
@@ -66,7 +76,7 @@ watch(value, (val) => {
 
 const handleCheckAll = (val) => {
   if (val.target.checked) {
-    value.value = ['All']
+    value.value = ['ALL']
     PortList.value.forEach((item) => { 
       item.checked = true
     })
@@ -112,6 +122,10 @@ const visibleTags = computed(() => {
 const hiddenTagsCount = computed(() => { 
   return Math.max(value.value.length - displayNumber.value, 0);
 })
+
+defineExpose({
+  initGetPortsList
+})
 </script>
 <template>
   <div style="width: 100%;">
@@ -125,14 +139,14 @@ const hiddenTagsCount = computed(() => {
       @change="handelchangeSelect"
     >
       <template #header>
-        <a-checkbox v-model:checked="checkAll" @change="handleCheckAll">All</a-checkbox>
+        <a-checkbox v-model:checked="checkAll" @change="handleCheckAll">ALL</a-checkbox>
       </template>
       <template #tag>
         <!-- 显示前3个标签 -->
         <el-tag 
           v-for="(tag, index) in visibleTags" 
           :key="index"
-          :closable="!tag.includes('All')" 
+          :closable="!tag.includes('ALL')" 
           @close="handelRemoveTag(tag)"
           class="tag"
         >

+ 20 - 13
src/views/DestinationDelivery/src/components/ConfiguRations/src/components/SetBookingWindow.vue

@@ -7,27 +7,37 @@ const beforeETAdays = ref('')
 const afterETAdays = ref('')
 
 const props = defineProps({
-  windowradio: {
-    type: Number,
-    default: 0
-  },
   setbookingdata: {
     type: Object,
     default: () => ({})
   }
 })
 
-const windowradio = ref(props.windowradio)
+const windowradio = ref()
 const setbookingdata = ref(props.setbookingdata)
+const initBookingWindowData = () => {
+  if(setbookingdata.value) {
+    windowradio.value = setbookingdata.value.windowradio
+  if(windowradio.value == 2) {
+    isBookingETD.value = true
+    beforeETDdays.value = setbookingdata.value.windowBeforeDays
+    afterETDdays.value = setbookingdata.value.windowAfterDays
+  } else if (windowradio.value == 3) {
+    isBookingETA.value = true
+    beforeETAdays.value = setbookingdata.value.windowBeforeDays
+    afterETAdays.value = setbookingdata.value.windowAfterDays
+  }
+  }
+}
 
 watch(() => props.setbookingdata, (val) => { 
   setbookingdata.value = val
-})
+  initBookingWindowData()
+}, { immediate: true, deep: true })
 
 // 选择booking window
 const emits = defineEmits(['changeBookingWindow'])
 const ChangeFrequency = (val: any) => {
-  let str = ''
   if(val == 1) {
     isBookingETD.value = false
     isBookingETA.value = false
@@ -35,22 +45,19 @@ const ChangeFrequency = (val: any) => {
     afterETDdays.value = ''
     beforeETAdays.value = ''
     afterETAdays.value = ''
-    str = 'No Specific time restrictions for creating booking'
-    emits('changeBookingWindow',windowradio.value, beforeETAdays.value, afterETAdays.value, str)
+    emits('changeBookingWindow',windowradio.value, beforeETAdays.value, afterETAdays.value)
   } else if(val == 2) {
     isBookingETD.value = true
     isBookingETA.value = false
     beforeETAdays.value = ''
     afterETAdays.value = ''
-    str = 'ETD/ATD: ' + beforeETDdays.value + ' days before to ' + afterETDdays.value + ' days after'
-    emits('changeBookingWindow',windowradio.value, beforeETDdays.value, afterETDdays.value, str)
+    emits('changeBookingWindow',windowradio.value, beforeETDdays.value, afterETDdays.value)
   } else if(val == 3) {
     isBookingETD.value = false
     isBookingETA.value = true
     beforeETDdays.value = ''
     afterETDdays.value = ''
-    str = 'ETA/ATA: ' + beforeETAdays.value + ' days before to ' + afterETAdays.value + ' days after'
-    emits('changeBookingWindow',windowradio.value, beforeETAdays.value, afterETAdays.value, str)
+    emits('changeBookingWindow',windowradio.value, beforeETAdays.value, afterETAdays.value)
   } else {
     isBookingETD.value = false
     isBookingETA.value = false

BIN
src/views/DestinationDelivery/src/components/ConfiguRations/src/images/default_configuration@2x.png


+ 252 - 72
src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue

@@ -5,63 +5,49 @@ 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 submitsucessful from './images/icon_success_big@2x.png'
 import { useUserStore } from '@/stores/modules/user'
+import { useRouter } from 'vue-router'
 
 const userStore = useUserStore()
+const router = useRouter()
+const { currentRoute } = router
+const { value } = currentRoute
+const { query } = value
+const { a } = query
 
 const CreateNewBOokingSearch = ref('')
+const DateValue = ref('')
+const bookingTableRef = ref()
 const VesselName = ref([])
+const VesselNametest = 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 NoEligibleVisible = ref(false)
+const isDisabled = ref(false)
+const SaveedVisible = ref(false)
+const UnableSaveVisible = ref(false)
+const missingmessage = ref('')
 const ATATimeList = ref(null)
 const ETATimeList = ref(null)
 const Addressradio = ref()
 const LocationName = ref('')
 const AddressLine1 = ref('')
 const AddressLine2 = ref('')
+const CountryCity = 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 selectedAddressList = ref()
+const isselectedAddress = ref(null)
+const checkShipmentsSubmitInfo = ref({})
 const ModeType = ref([
   {
     label: 'Truck',
@@ -71,29 +57,27 @@ const ModeType = ref([
 
 // 设置无法点击
 const isNotClickAddress = computed(() => {
-  return NoPermissionVisible.value || NoEligibleVisible.value
+  return NoPermissionVisible.value || NoEligibleVisible.value || isDisabled.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 isNotSubmit = computed(() => {
+  if(NoPermissionVisible.value || NoEligibleVisible.value || isDisabled.value) {
+    return true
   }
-])
+
+  if(
+    Object.keys(checkShipmentsSubmitInfo.value).length == 0 ||
+    selectedAddressList.value == undefined ||
+    modetypeValue.value == '' ||
+    DateValue.value == ''
+  ) {
+    return true
+  }
+  return false;
+})
+
+const ManageAddressList = ref([])
 // 需要特殊样式的日期列表
 const specialDates = ref([
   '2025-05-27',
@@ -110,7 +94,8 @@ const DateRangeChangeATA = (val:any) => {
 }
 
 const showLabel = computed(() => {
-  return VesselName.value.length!= 0 || isFocused.value
+  // return VesselName.value.length!= 0 || isFocused.value
+  return VesselNametest.value!= '' || isFocused.value
 })
 
 const showataLabel = computed(() => {
@@ -125,6 +110,10 @@ const changeFocus = (val: boolean) => {
   isFocused.value = val
 }
 
+const changeFocustest = (val: boolean) => {
+  isFocused.value = val
+}
+
 const DateChangeFocusATA = (val: boolean) => {
   isataFocused.value = val
 }
@@ -134,7 +123,7 @@ const DateChangeFocusETA = (val: boolean) => {
 
 // 特殊日期样式
 const getCurrentStyle = (current: any) => {
-  const dateString = current.format('YYYY-MM-DD');
+  const dateString = current.format('YYYY.MM.DD');
   if (specialDates.value.includes(dateString)) {
     return { 
       background: '#b3e5d4', 
@@ -146,14 +135,37 @@ const getCurrentStyle = (current: any) => {
   // 默认样式
   return {};
 }
-
+// 保存新地址
+const SaveNewAddress = () => {
+  if(
+    LocationName.value != '' &&
+    AddressLine1.value != '' &&
+    AddressLine2.value != '' &&
+    CountryCity.value != '' &&
+    PostalCode.value != '' &&
+    ContactPerson.value != '' &&
+    ContactNumber.value != ''
+  ) {
+    ManageAddressList.value.push({
+      location_name: LocationName.value,
+      address_1: AddressLine1.value,
+      address_2: AddressLine2.value,
+      country: CountryCity.value,
+      city: CountryCity.value,
+      postal_code: PostalCode.value,
+      contact_person: ContactPerson.value,
+      contact_number: ContactNumber.value,
+      notes: instructions.value,
+      deliver_address_is_new: 'yes'
+    })
+    AddNewAddressVisible.value = false
+  }
+}
 // 点击按钮
 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) {
@@ -162,6 +174,132 @@ const changeAddressRadio = () => {
   }
   selectedAddressList.value = ManageAddressList.value[Addressradio.value]
 }
+// 页面初始化
+const getInitBookingData = () => {
+  $api
+  .InitCreateBooking({
+    a: a != undefined ? a: ''
+  })
+  .then((res: any) => {
+    if (res.code === 200) {
+      if(res.data.msg.includes('No Eligible Shipments for Booking')) {
+        NoEligibleVisible.value = true
+        isDisabled.value = true
+      }
+      if(res.data.msg.includes('success')) {
+        bookingTableRef.value.getTableData(res.data.data.tableData)
+      }
+    }
+  })
+}
+// 查询Shipments
+const SearchShipment = () => {
+  $api
+  .BookingTableSearch({
+    serial_no: a != undefined ? a: '',
+    text_search: CreateNewBOokingSearch.value,
+    vessel: VesselNametest.value,
+    eta_start: ETATimeList.value.length != 0 ? ETATimeList.value[0] : '',
+    eta_end: ETATimeList.value.length != 0 ? ETATimeList.value[1] : '',
+    ata_start: ATATimeList.value.length != 0 ? ATATimeList.value[0] : '',
+    ata_end: ATATimeList.value.length != 0 ? ATATimeList.value[1] : ''
+
+  })
+  .then((res: any) => {
+    if (res.code === 200) {
+      console.log(res.data)
+    }
+  })
+}
+
+// 选择shipments获取address book
+const isrecommenddate = ref()
+// 判断date是否一致
+const areAllDateRangesSame = (date: any) => { 
+  const firstRange = date[0].date_range;
+  return date.every(item => {
+    const currentRange = item.date_range;
+    return (
+      currentRange.length === firstRange.length &&
+      currentRange.every((date, index) => date === firstRange[index])
+    );
+  });
+}
+// 遍历日期
+const getDateRangeArray = (startDateStr, endDateStr) => {
+  const parseDate = (str) => {
+    const [year, month, day] = str.split('.').map(Number);
+    return new Date(year, month - 1, day); // 月份从0开始计数
+  };
+
+  const startDate = parseDate(startDateStr);
+  const endDate = parseDate(endDateStr);
+
+  const dateArray = [];
+  const currentDate = new Date(startDate);
+  
+  while (currentDate <= endDate) {
+    // 格式化为 YYYY.MM.DD
+    const formattedDate = [
+      currentDate.getFullYear(),
+      (currentDate.getMonth() + 1).toString().padStart(2, '0'),
+      currentDate.getDate().toString().padStart(2, '0')
+    ].join('.');
+    dateArray.push(formattedDate);
+    currentDate.setDate(currentDate.getDate() + 1);
+  }
+
+  return dateArray;
+}
+const selectChangeEvent = (val: any, date: any, submitInfo: any) => {
+  checkShipmentsSubmitInfo.value = submitInfo
+  if(date.length != 0) {
+    isrecommenddate.value = areAllDateRangesSame(date)
+    if(isrecommenddate.value) {
+      specialDates.value = getDateRangeArray(date[0].date_range[0],date[0].date_range[1])
+    }
+  } else {
+    specialDates.value = []
+  }
+  $api
+  .getAddressBookList({
+    ...val,
+
+  })
+  .then((res: any) => {
+    if (res.code === 200) {
+      ManageAddressList.value = res.data
+    }
+  })
+}
+
+// 保存
+const SubmitBooking = () => {
+  $api
+  .SaveBookingList({
+    ...checkShipmentsSubmitInfo.value,
+    ...selectedAddressList.value,
+    delivery_mode: modetypeValue.value,
+    delivery_date: DateValue.value,
+    special_requirements: Requirements.value
+  })
+  .then((res: any) => {
+    if (res.code === 200 && res.data.msg == 'success') {
+      SaveedVisible.value = true
+      setTimeout(() => {
+        SaveedVisible.value = false
+        router.push({ name: 'Destination Delivery'})
+      }, 3000)
+    } else {
+        UnableSaveVisible.value = true
+        missingmessage.value = res.data.msg
+    }
+  })
+}
+
+onMounted(() => { 
+  getInitBookingData()
+})
 </script>
 
 <template>
@@ -170,7 +308,7 @@ const changeAddressRadio = () => {
       <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>
+        <el-button :disabled="isNotSubmit" @click="SubmitBooking" class="el-button--main create-button"><span class="font_family icon-icon_submit_b"></span> Submit</el-button>
       </div>
     </div>
     <!-- Select Shipments -->
@@ -199,12 +337,22 @@ const changeAddressRadio = () => {
           <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>
+          <!-- <AutoSelect ASPlaceholder="Input Vessel Name" :ASValue="VesselName" @changeFocus="changeFocus"></AutoSelect> -->
+          <el-input
+            placeholder="Input Vessel Name"
+            v-model="VesselNametest"
+            @focus="changeFocustest(true)"
+            @blur="changeFocustest(false)"
+            class="log_input"
+          >
+          </el-input>
           <span v-if="showLabel" class="border-label">Vessel Name</span>
         </div>
-        <el-button class="el-button--dark create-button">Search</el-button>
+        <el-button class="el-button--dark create-button" @click="SearchShipment">Search</el-button>
       </div>
-      <NewbookingTable :ColumnsList="NewBookingColumnsList"></NewbookingTable>
+      <NewbookingTable ref="bookingTableRef" 
+        @selectChangeEvent="selectChangeEvent"
+      ></NewbookingTable>
     </div>
     <!-- Delivery Information -->
     <div class="Delivery">Delivery Information</div>
@@ -221,14 +369,14 @@ const changeAddressRadio = () => {
         <el-radio>
           <div>
             <div class="radio_top">
-              <div class="radio_title">{{ selectedAddressList.title }}</div>
+              <div class="radio_title">{{ selectedAddressList.location_name }}</div>
             </div>
             <div class="radio_content">
-              <div class="radio_content_text">{{ selectedAddressList.Address1 }}</div>
-              <div class="radio_content_text">{{ selectedAddressList.Address2 }}</div>
+              <div class="radio_content_text">{{ selectedAddressList.address_1 }}</div>
+              <div class="radio_content_text">{{ selectedAddressList.country }},{{ selectedAddressList.city }} {{ selectedAddressList.postal_code }}</div>
             </div>
             <div class="radio_bottom radio_content_text">
-              {{ selectedAddressList.Contact }}
+              Contact: {{ selectedAddressList.contact_person }} {{ selectedAddressList.contact_number }}
             </div>
           </div>
         </el-radio>
@@ -253,6 +401,7 @@ const changeAddressRadio = () => {
         <div>
         <div class="delivery_type_title"><span class="stars_red">*</span>Preferred Delivery Date</div>
         <a-date-picker
+          v-model:value ="DateValue"
           :disabled="isNotClickAddress"
           :showToday="false"
           style="width: 240px;"
@@ -290,18 +439,18 @@ const changeAddressRadio = () => {
         <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 class="radio_title">{{ item.location_name }}</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 class="radio_content_text">{{ item.address_1 }}</div>
+              <div class="radio_content_text">{{ item.country }},{{ item.city }} {{ item.postal_code }}</div>
             </div>
             <div class="radio_bottom radio_content_text">
-              {{ item.Contact }}
+              Contact: {{ item.contact_person }} {{ item.contact_number }}
             </div>
           </div>
         </el-radio>
@@ -336,7 +485,7 @@ const changeAddressRadio = () => {
       <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>
+          <el-input class="inputmargin" placeholder="Unit number, floor, etc. (optional)" v-model="CountryCity"></el-input>
         </div>
         <div style="width: 50%;">
           <div class="diaolog_add_title"><span class="stars_red">*</span>Postal Code</div>
@@ -359,7 +508,7 @@ const changeAddressRadio = () => {
       <template #footer>
         <div class="dialog-footer" style="justify-content: end;">
           <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">
+          <el-button class="el-button--dark dialog-button" @click=SaveNewAddress>
             Save Address
           </el-button>
         </div>
@@ -403,6 +552,37 @@ const changeAddressRadio = () => {
         </div>
       </template>
     </el-dialog>
+    <!-- 保存成功 -->
+    <el-dialog v-model="SaveedVisible" width="320" style="height: 212px">
+      <div style="text-align: center"><el-image :src="submitsucessful" style="width: 64px;" /></div>
+      <div style="text-align: center; margin-top: 20px">Saved successfully</div>
+    </el-dialog>
+    <!-- 保存失败 -->
+    <el-dialog v-model="UnableSaveVisible" width="480">
+      <div>{{ missingmessage }}</div>
+      <div>Please complete all required fields.</div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button
+            class="el-button--danger"
+            @click="UnableSaveVisible = false"
+            style="width: 100px"
+          >
+            OK
+          </el-button>
+        </div>
+      </template>
+      <template #header>
+        <div class="cancel_header">
+          <span class="iconfont_icon iconfont_warning">
+            <svg class="iconfont icon_danger" aria-hidden="true">
+              <use xlink:href="#icon-icon_fail_fill_b"></use>
+            </svg>
+          </span>
+          Unable to Save
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 

+ 71 - 65
src/views/DestinationDelivery/src/components/CreateNewBooking/src/components/NewbookingTable.vue

@@ -4,20 +4,12 @@ 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 tableLoadingColumn = ref(false)
 const isNotActivated = ref(false)
 
+const recommendedDate = ref([])
+
 const tableData = ref<VxeGridProps<any>>({
   border: true,
   round: true,
@@ -41,7 +33,8 @@ const handleColumns = (columns: any) => {
   const newColumns = columns.map((item: any) => {
     let curColumn: any = {
       title: item.title,
-      field: item.field
+      field: item.field,
+      width: '150px'
     }
     // 格式化
     if (item.formatter === 'date') {
@@ -54,6 +47,14 @@ const handleColumns = (columns: any) => {
         ...curColumn,
         slots: { default: 'trackingNo' }
       }
+    } else if (item.type == 'recommend') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) => {
+          const array = cellValue.split("-")
+          return `${formatTimezone(array[0])} - ${formatTimezone(array[1])}`
+        }
+      }
     }
     return curColumn
   })
@@ -61,67 +62,55 @@ const handleColumns = (columns: any) => {
 }
 // 获取表格列
 const getTableColumns = async () => {
-  tableData.value.columns = handleColumns(columnstest.value)
-  tableData.value.columns?.unshift({
-    type: 'checkbox',
-    width: 40
+  tableLoadingColumn.value = true
+  await $api.BookingTableColumn().then((res: any) => {
+    if (res.code === 200) {
+      tableData.value.columns = [
+        { type: 'checkbox', width: 50, fixed: 'left' },
+        ...handleColumns(res.data.TrackingTableColumns)
+      ]
+    }
+  })
+  nextTick(() => {
+    tableLoadingColumn.value = false
   })
 }
 // 获取表格数据
 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'
-    // }
-  ]
+  tableData.value.data = val
 }
 
+const emits = defineEmits(['selectChangeEvent'])
 // 选中
+let checkShipments = []
+let checkShipmentsSubmit = []
+let checkRecommend = []
+let checkShipmentsdata = []
+let checkShipmentsInfo = {}
+let checkShipmentsSubmitInfo = {}
 const selectChangeEvent = () => {
   const $grid = tableRef.value
   if ($grid) {
     const records = $grid.getCheckboxRecords()
-    console.log(records)
+    checkShipments = records.map(item => ({ consignee_id: item.consignee_id }))
+    checkRecommend = records.map(item => ({ date_range: item.date_range.split('-') }))
+    if(checkShipments.length != 0) {
+      checkShipmentsdata = Object.keys(checkShipments?.[0])
+      checkShipmentsSubmit = Object.keys(records?.[0])
+      checkShipmentsdata.forEach((item) => {
+        Object.assign(checkShipmentsInfo, {
+          [item]: checkShipments.map((row) => row[item] )
+        })
+      })
+      checkShipmentsSubmit.forEach((item) => {
+        Object.assign(checkShipmentsSubmitInfo, {
+          [item]: records.map((row) => row[item] )
+        })
+      })
+    } else {
+      checkShipmentsSubmitInfo = {}
+    }
+    emits('selectChangeEvent',checkShipmentsInfo, checkRecommend,checkShipmentsSubmitInfo)
   }
 }
 // 全选
@@ -129,7 +118,25 @@ const selectAllChangeEvent= () => {
   const $grid = tableRef.value
   if ($grid) {
     const records = $grid.getCheckboxRecords()
-    console.log(records)
+    checkShipments = records.map(item => ({ consignee_id: item.consignee_id }))
+    checkRecommend = records.map(item => ({ date_range: item.date_range.split('-') }))
+    if(checkShipments.length != 0) {
+      checkShipmentsdata = Object.keys(checkShipments?.[0])
+      checkShipmentsSubmit = Object.keys(records?.[0])
+      checkShipmentsdata.forEach((item) => {
+        Object.assign(checkShipmentsInfo, {
+          [item]: checkShipments.map((row) => row[item] )
+        })
+      })
+      checkShipmentsSubmit.forEach((item) => {
+        Object.assign(checkShipmentsSubmitInfo, {
+          [item]: records.map((row) => row[item] )
+        })
+      })
+    } else {
+      checkShipmentsSubmitInfo = {}
+    }
+    emits('selectChangeEvent',checkShipmentsInfo, checkRecommend,checkShipmentsSubmitInfo)
   }
 }
 
@@ -138,7 +145,6 @@ useRowClickStyle(tableRef)
 
 onMounted(() => {
   getTableColumns()
-  getTableData(1)
 })
 defineExpose({
   getTableData
@@ -152,7 +158,7 @@ defineExpose({
       ref="tableRef"
       :style="{ border: 'none' }"
       v-bind="tableData"
-      v-vloading="tableLoadingTable"
+      v-loading="tableLoadingTable || tableLoadingColumn"
       @checkbox-change="selectChangeEvent"
       @checkbox-all="selectAllChangeEvent"
     >

BIN
src/views/DestinationDelivery/src/components/CreateNewBooking/src/images/icon_success_big@2x.png


+ 12 - 1
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -8,6 +8,8 @@ import { formatTimezone, formatNumber } from '@/utils/tools'
 import BookingDetailDialog from './components/BookingDetailDialog.vue'
 import EmailDialog from './components/EmailDialog.vue'
 import TipsDialog from './components/TipsDialog.vue'
+import { useRouter } from 'vue-router'
+const router = useRouter()
 
 const props = defineProps({
   height: {
@@ -274,6 +276,15 @@ const clickEmailBtn = (row: any) => {
   emailDialogRef.value.openDialog(row)
 }
 
+// edit
+const handelEdit = (row: any) => {
+  console.log(row)
+  router.push({
+    path: '/destination-delivery/CreateNewBooking',
+    query: { a: row._serial_no}
+  })
+}
+
 const handleCreate = () => {
   // Handle create new booking logic here
   console.log('Create new booking')
@@ -347,7 +358,7 @@ defineExpose({
         </el-button>
         <!-- edit -->
         <el-button
-          @click="tipsDialogModel = true"
+          @click="handelEdit(row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
         >

+ 0 - 2
src/views/Tracking/src/components/TrackingTable/src/components/VGMView.vue

@@ -317,7 +317,6 @@ const handleSave = () => {
       ...item
     }
   })
-
   let variableList = []
   let tableInfo = {}
   if (tableRowData.length !== 0) {
@@ -335,7 +334,6 @@ const handleSave = () => {
       })
     })
   }
-
   $api
     .saveVGMData({
       serial_no: allData.value.serial_no,