AmandaG 4 сар өмнө
parent
commit
459612279a
18 өөрчлөгдсөн 334 нэмэгдсэн , 215 устгасан
  1. 30 0
      src/api/module/Delivery.ts
  2. 2 1
      src/auto-imports.d.ts
  3. 18 0
      src/components/VTag/src/VTag.vue
  4. 15 2
      src/styles/theme.scss
  5. 1 1
      src/views/Booking/src/components/BookingDetail/src/components/AddReferenceDialog.vue
  6. 1 1
      src/views/ChatLog/src/components/TableView/src/TableView.vue
  7. 1 0
      src/views/DestinationDelivery/src/DestinationDelivery.vue
  8. 13 11
      src/views/DestinationDelivery/src/components/DeliveryDate.vue
  9. 67 77
      src/views/DestinationDelivery/src/components/TableView/src/TableView.vue
  10. 24 37
      src/views/DestinationDelivery/src/components/TableView/src/components/BookingDetailDialog.vue
  11. 9 4
      src/views/DestinationDelivery/src/components/TableView/src/components/DetailStep.vue
  12. 37 20
      src/views/DestinationDelivery/src/components/TableView/src/components/EmailDialog.vue
  13. 26 17
      src/views/DestinationDelivery/src/components/TableView/src/components/OperationLogProcess.vue
  14. 7 1
      src/views/DestinationDelivery/src/components/TableView/src/components/ShipmentInforTable.vue
  15. 56 21
      src/views/DestinationDelivery/src/components/TableView/src/components/TipsDialog.vue
  16. 1 1
      src/views/Tracking/src/components/TrackingDetail/src/components/AddReferenceDialog.vue
  17. 1 0
      src/views/Tracking/src/components/TrackingDetail/src/components/AttachmentView.vue
  18. 25 21
      src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

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

@@ -288,4 +288,34 @@ export const getAddressCountryCityData = (params: any, config: any) => {
     },
     config
   )
+}
+
+/**  
+ * 获取delivery date组件中展示数据
+ */
+export const getDeliveryDateData = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_booking',
+      operate: 'delivery_date_load',
+      ...params
+    },
+    config
+  )
+}
+
+/**
+ * 获取delivery email部分records
+ */
+export const getEmailRecords = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_booking',
+      operate: 'email_message_init',
+      ...params
+    },
+    config
+  )
 }

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

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

+ 18 - 0
src/components/VTag/src/VTag.vue

@@ -11,10 +11,14 @@ interface internalProps {
     | 'Completed'
     | 'Departed'
     | 'Pending Approval'
+    | 'Approved'
+    | 'Rejected'
   large?: boolean
 }
 
 const mappingTable = new Map([
+  ['Approved', 'approved'],
+  ['Rejected', 'rejected'],
   ['Confirmed', 'confirmed'],
   ['Cancelled', 'cancelled'],
   ['Created', 'created'],
@@ -131,6 +135,20 @@ defineProps<internalProps>()
       background-color: var(--color-tag-unfinished-approval);
     }
   }
+  &.v-tag__approved {
+    background-color: var(--color-tag-approved-bg);
+    color: var(--color-tag-approved);
+    .dot {
+      background-color: var(--color-tag-approved);
+    }
+  }
+  &.v-tag__rejected {
+    background-color: var(--color-tag-rejected-bg);
+    color: var(--color-tag-rejected);
+    .dot {
+      background-color: var(--color-tag-rejected);
+    }
+  }
   & + .v-tag {
     margin-left: 8px;
   }

+ 15 - 2
src/styles/theme.scss

@@ -73,9 +73,15 @@
   --color-tag-arrived-bg: #e7faf8;
   --color-tag-completed-bg: #e8fbe4;
   --color-tag-Departed-bg: #d9edfa;
-  --color-tag-unfinished-approval-bg: #fbfbfe;
+  --color-tag-unfinished-approval-bg: #fff4d1;
   --color-tag-unfinished-approval: #e0a100;
 
+  --color-tag-approved-bg: rgb(232, 251, 228);
+  --color-tag-approved: rgb(91, 180, 98);
+
+  --color-tag-rejected-bg: rgba(201, 53, 63,0.2);
+  --color-tag-rejected: rgb(201, 53, 63);
+
   --color-border: #eaebed;
   --color-select-border: #eaebed;
   --border-color-2: #eaebed;
@@ -328,6 +334,7 @@
   --color-steps-current-icon-bg: #fff4d1;
   --color-steps-rejected-bg: #fedcde;
   --color-steps-approved-bg: #e8fbe4;
+  --color-steps-cancelled-bg: #ebeef1;
   --color-booking-info-linear-bg: linear-gradient(90deg, #c4c9ee 0%, #e8e8ff 49.52%, #bfe1ff 100%);
   --color-process-data-value-bg: #e8ebef;
 
@@ -342,6 +349,7 @@
 
   --color-guide-icon-bg: rgba(237, 237, 237, 0.6);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.45);
+  --color--process-data-tips-bg: #e8ebef;
 }
 
 :root.dark {
@@ -537,7 +545,9 @@
     }
   }
 
-  --color-tag-unfinished-approval-bg: #eeeff6;
+  --color-tag-unfinished-approval-bg: rgba(224, 161, 0, 0.2);
+  --color-tag-approved-bg: rgba(91, 180, 98, 0.2);
+  --color-tag-rejected-bg: rgb(255, 220, 222);
 
   --color-card-icon-box-bg: #4f535c;
   --color-card-number-cancelled: #babcc0;
@@ -546,6 +556,8 @@
   --color-steps-current-icon-bg: #534b30;
   --color-steps-rejected-bg: #4f353d;
   --color-steps-approved-bg: #394e44;
+  --color-steps-cancelled: #c2c4c7;
+  --color-steps-cancelled-bg: rgba(240, 241, 243, 0.2);
   --color-booking-info-linear-bg: linear-gradient(90deg, #636db7 0%, #515195 49.52%, #7b9bc9 100%);
   --color-process-data-value-bg: #4f5760;
 
@@ -560,4 +572,5 @@
 
   --color-guide-icon-bg: rgba(237, 237, 237, 0.1);
   --color-guide-icon-hover-bg: rgba(237, 237, 237, 0.15);
+  --color--process-data-tips-bg: #4f5760;
 }

+ 1 - 1
src/views/Booking/src/components/BookingDetail/src/components/AddReferenceDialog.vue

@@ -39,7 +39,7 @@ const tableData = ref<VxeGridProps<any>>({
     {
       title: 'Action',
       width: 90,
-      fixed: 'right',
+      fixed: 'left',
       slots: { default: 'action' }
     }
   ],

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

@@ -68,7 +68,7 @@ const getTableColumns = async () => {
       tableData.value.columns = [
         { type: 'checkbox', width: 50, fixed: 'left' },
         ...handleColumns(res.data.OperationTableColumns),
-        { title: 'Action', width: 106, fixed: 'right', slots: { default: 'action' } }
+        { title: 'Action', width: 106, fixed: 'left', slots: { default: 'action' } }
       ]
       tableOriginColumnsField.value = res.data.OperationTableColumns
     }

+ 1 - 0
src/views/DestinationDelivery/src/DestinationDelivery.vue

@@ -106,6 +106,7 @@ const handleSearch = () => {
           style="height: 38px"
           class="el-button--main el-button--pain-theme"
           @click="handleCreate"
+          v-if="tableRef?.isEmployeeRole === false"
         >
           <span style="margin-right: 4px" class="font_family icon-icon_add_b"></span>
           <span style="font-weight: 400">Create New Booking</span>

+ 13 - 11
src/views/DestinationDelivery/src/components/DeliveryDate.vue

@@ -36,19 +36,21 @@ watch(
   },
   { immediate: true, deep: true }
 )
-
-const stateDataList = {
-  '2025-06-01': {
-    pending: 3,
-    approved: 3
-  },
-  '2025-05-20': {
-    pending: 2,
-    approved: 1
-  }
+const getDeliveryDateData = () => {
+  $api.getDeliveryDateData().then((res) => {
+    if (res.code === 200) {
+      const data = res.data.data
+      stateDataList.value = data
+    }
+  })
 }
+onMounted(() => {
+  getDeliveryDateData()
+})
+
+const stateDataList = ref({})
 const isShowStatus = (date: string) => {
-  return stateDataList[date] ? true : false
+  return stateDataList.value[date] ? true : false
 }
 
 const emit = defineEmits(['DateRangeChange', 'DateChange'])

+ 67 - 77
src/views/DestinationDelivery/src/components/TableView/src/TableView.vue

@@ -37,10 +37,10 @@ const handleColumns = (columns: any, status?: string) => {
         ...curColumn,
         slots: { default: 'status' }
       }
-    } else if (item.type === 'link' && status !== 'all') {
+    } else if (item.type === 'multiple_link') {
       curColumn = {
         ...curColumn,
-        slots: { default: 'link' }
+        slots: { default: 'multLink' }
       }
     }
     // 格式化
@@ -49,6 +49,17 @@ const handleColumns = (columns: any, status?: string) => {
         ...curColumn,
         formatter: ({ cellValue }: any) => formatTimezone(cellValue)
       }
+    } else if (item.formatter === 'range') {
+      curColumn = {
+        ...curColumn,
+        formatter: ({ cellValue }: any) => {
+          if (!cellValue) return '--'
+          const rangeData = cellValue.split(';')
+          const leftDate = rangeData[0] ? formatTimezone(rangeData[0]) : '_'
+          const rightDate = rangeData[1] ? formatTimezone(rangeData[0]) : '_'
+          return leftDate + ' -- ' + rightDate
+        }
+      }
     }
     return curColumn
   })
@@ -60,16 +71,12 @@ const getTableColumns = async () => {
   tableLoadingColumn.value = true
   await $api.getDeliveryBookingTableColumn().then((res: any) => {
     if (res.code === 200) {
-      tableData.value.columns = [
-        { type: 'checkbox', width: 50, fixed: 'left' },
-        ...handleColumns(res.data.TrackingTableColumns)
-      ]
-      console.log('tableData.value.columns', tableData.value.columns)
+      tableData.value.columns = [...handleColumns(res.data.TrackingTableColumns)]
       const index = tableData.value.columns.findIndex((item: any) => item.title === 'Action')
       if (index === -1) {
         tableData.value.columns.push({
           title: 'Action',
-          fixed: 'right',
+          fixed: 'left',
           width: 130,
           slots: { default: 'action' }
         })
@@ -97,7 +104,7 @@ const assignTableData = (data: any) => {
     if (index === -1) {
       tableData.value.columns.push({
         title: 'Action',
-        fixed: 'right',
+        fixed: 'left',
         width: 130,
         slots: { default: 'action' }
       })
@@ -105,7 +112,7 @@ const assignTableData = (data: any) => {
   }
 }
 
-const isEmployeeRole = ref(true)
+const isEmployeeRole = ref(null)
 
 const emit = defineEmits(['getNumberCards'])
 const rtnNumberCards = (data) => {
@@ -156,7 +163,7 @@ const getTableData = async (isPageChange?: boolean) => {
     })
     .then((res: any) => {
       if (res.code === 200) {
-        isEmployeeRole.value = res.data.is_employee
+        isEmployeeRole.value = res.data.is_employee || false
         assignTableData(res.data)
         rtnNumberCards(res.data)
       }
@@ -180,7 +187,7 @@ const SearchOperationLog = () => {
     })
     .then((res: any) => {
       if (res.code === 200) {
-        isEmployeeRole.value = res.data.is_employee
+        isEmployeeRole.value = res.data.is_employee || false
         assignTableData(res.data)
         rtnNumberCards(res.data)
       }
@@ -273,30 +280,6 @@ const tableData = ref<VxeGridProps<any>>({
 // 实现行点击样式
 useRowClickStyle(tableRef)
 
-const CustomizeColumnsRef = ref()
-// 打开定制表格弹窗
-const handleCustomizeColumns = () => {
-  const params = {
-    getData: {
-      action: 'ocean_booking',
-      operate: 'setting_display'
-    },
-    saveData: {
-      action: 'ajax',
-      operate: 'save_setting_display',
-      model_name: 'Booking_Search'
-    }
-  }
-  CustomizeColumnsRef.value.openDialog(params, -220)
-}
-// 定制表格
-const customizeColumns = async () => {
-  await getTableColumns()
-  nextTick(() => {
-    tableRef.value && autoWidth(tableData.value, tableRef.value)
-  })
-}
-
 const tableLoadingColumn = ref(false)
 const tableLoadingTableData = ref(false)
 
@@ -316,7 +299,7 @@ const clickEmailBtn = (row: any) => {
 const handleEdit = (row: any) => {
   router.push({
     path: '/destination-delivery/CreateNewBooking',
-    query: { a: row._serial_no}
+    query: { a: row._serial_no }
   })
 }
 
@@ -324,28 +307,35 @@ const handleCreate = () => {
   router.push({ name: 'Create New Booking' })
 }
 
-const handleLinkClick = (row: any, column: any) => {
-  if (column.title === 'Booking No.') {
-    router.push({
-      path: '/booking/detail',
-      query: { a: row.__serial_no, _schemas: row._schemas, status: row.Status }
-    })
-    // visitedRowState.setBookingTableData(row['__serial_no'])
-  } else if (column.title === 'HBOL/HAWB No.') {
-    router.push({
-      path: '/tracking/detail',
-      query: { a: row.__serial_no, _schemas: row._schemas }
-    })
+const testData = [
+  {
+    key: 'A1703530062',
+    value: 'CD1Xeh4rG%2FPmYIiCHAw2%2B0Gw8BFZ8k%2F2pQDkYX3vuf6iZ3kcQXj%2BzQ'
+  },
+  {
+    key: 'A1703530062',
+    value: 'CD1Xeh4rG%2FPmYIiCHAw2%2B0Gw8BFZ8k%2F2pQDkYX3vuf6iZ3kcQXj%2BzQ'
   }
+]
+
+const handleMultLinkClick = (item: any) => {
+  router.push({
+    path: '/tracking/detail',
+    query: { a: item.value, _schemas: item.order_from }
+  })
 }
 
 const tipsDialogRef = ref()
-const handleTips = (type: string) => {
-  tipsDialogRef.value.openDialog(type)
+const handleTips = (type: string, row: any) => {
+  tipsDialogRef.value.openDialog(type, row)
+}
+const handleChangeRowState = () => {
+  SearchOperationLog()
 }
 
 defineExpose({
-  SearchOperationLog
+  SearchOperationLog,
+  isEmployeeRole
 })
 </script>
 
@@ -359,12 +349,6 @@ defineExpose({
   >
     <div class="table-tools">
       <div class="left-total-records">Booking List</div>
-      <div class="right-tools-btn">
-        <el-button type="default" @click="handleCustomizeColumns">
-          <span style="margin-right: 8px" class="font_family icon-icon_column_b"></span>
-          Customize Columns
-        </el-button>
-      </div>
     </div>
     <vxe-grid
       ref="tableRef"
@@ -383,6 +367,7 @@ defineExpose({
           <p>ensure smooth last-mile delivery.</p>
           <el-button
             style="height: 40px"
+            v-if="isEmployeeRole === false"
             class="el-button--main el-button--pain-theme"
             @click="handleCreate"
           >
@@ -392,8 +377,8 @@ defineExpose({
         </div>
       </template>
       <!-- action操作的插槽 -->
-      <template #action="{ row }">
-        <!-- view  -->
+      <template #action="{ row, rowIndex }">
+        <!-- view -->
         <el-button
           @click="clickViewBtn(row)"
           class="action-btn el-button--blue"
@@ -403,10 +388,10 @@ defineExpose({
         </el-button>
         <!-- email -->
         <el-button
+          v-if="!isEmployeeRole && row.status === 'Approved'"
           @click="clickEmailBtn(row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
-          v-if="isEmployeeRole && row.status === 'Approved'"
         >
           <span class="font_family icon-icon_email_b"> </span>
         </el-button>
@@ -415,32 +400,32 @@ defineExpose({
           @click="handleEdit(row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
-          v-if="isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="!isEmployeeRole && row.status === 'Pending Approval'"
         >
           <span class="font_family icon-icon_edit_b"> </span>
         </el-button>
         <!-- cancel -->
         <el-button
-          @click="handleTips('cancel')"
+          @click="handleTips('cancel', row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
-          v-if="isEmployeeRole && row.status === 'Pending Approval'"
+          v-if="!isEmployeeRole && row.status === 'Pending Approval'"
         >
           <span class="font_family icon-icon_cancelled_b"> </span>
         </el-button>
-        <!-- confirm -->
+        <!-- approve -->
         <el-button
-          v-if="!isEmployeeRole && row.status === 'Pending Approval'"
-          @click="handleTips('confirm')"
+          @click="handleTips('approve', row)"
           class="action-btn el-button--blue"
+          v-if="isEmployeeRole && row.status === 'Pending Approval'"
           style="height: 24px; width: 24px"
         >
           <span class="font_family icon-icon_confirm_b"> </span>
         </el-button>
         <!-- reject -->
         <el-button
-          v-if="!isEmployeeRole && row.status === 'Pending Approval'"
-          @click="handleTips('reject')"
+          v-if="isEmployeeRole && row.status === 'Pending Approval'"
+          @click="handleTips('reject', row)"
           class="action-btn el-button--blue"
           style="height: 24px; width: 24px"
         >
@@ -452,13 +437,19 @@ defineExpose({
         <VTag :type="row[column.field]">{{ row[column.field] }}</VTag>
       </template>
       <!-- Booking No字段的插槽 -->
-      <template #link="{ row, column }">
-        <span
-          style="color: var(--color-theme); cursor: pointer"
-          @click="handleLinkClick(row, column)"
+      <template #multLink="{ row, column }">
+        <div
+          style="display: inline-block"
+          v-for="(item, index) in row[column.field]"
+          :key="item.key"
         >
-          {{ row[column.field] }}
-        </span>
+          <span v-if="index > 0">、</span>
+          <span
+            style="color: var(--color-theme); cursor: pointer"
+            @click="handleMultLinkClick(item)"
+            >{{ item.key }}</span
+          >
+        </div>
       </template>
     </vxe-grid>
 
@@ -479,10 +470,9 @@ defineExpose({
       </div>
     </div>
 
-    <CustomizeColumns @customize="customizeColumns" ref="CustomizeColumnsRef" />
     <BookingDetailDialog ref="bookingDetailDiaRef" />
     <EmailDialog ref="emailDialogRef" />
-    <TipsDialog ref="tipsDialogRef" type="reject" booking-no="123" />
+    <TipsDialog ref="tipsDialogRef" @state-change="handleChangeRowState" />
   </div>
 </template>
 

+ 24 - 37
src/views/DestinationDelivery/src/components/TableView/src/components/BookingDetailDialog.vue

@@ -8,24 +8,22 @@ const visible = ref(false)
 const loading = ref(false)
 
 const shipmentInfoTableData = ref([])
+const processList = ref()
+const rowData = ref()
 const openDialog = (row: any) => {
   visible.value = true
   loading.value = true
   $api
     .getDeliveryBookingDialogData({ serial_no: row._serial_no })
     .then((res) => {
-      console.log('res', res)
       const data = res.data.data
+      let status = data.status
       if (data.update_time !== data.create_time && data.status === 'Pending Approval') {
-        data.status = 'Modified'
-      }
-      handleStepData(data.status, data)
-      deliveryInfoData.value = {
-        modeType: data.delivery_mode || '--',
-        deliveryDate: data.delivery_date || '--',
-        deliveryAddress: data.delivery_address || '--',
-        specialRequirements: data.special_requirements || '--'
+        status = 'Modified'
       }
+      handleStepData(status, data)
+      processList.value = data.operation_log
+      rowData.value = data
       shipmentInfoTableData.value = data.shipmentsData || []
     })
     .finally(() => {
@@ -64,42 +62,33 @@ const handleStepData = (status, data) => {
   } else if (status === 'Approved') {
     stepList.value.push({
       label: 'Approved',
-      date: '--',
+      date: data.update_time || '--',
       icon: 'icon_confirm_b',
-      status: 'unfinished'
+      status: ''
     })
   } else if (status === 'Rejected') {
     stepList.value.push({
       label: 'Rejected',
-      date: 'Jun-05-2024',
+      date: data.update_time || '--',
       icon: 'icon_reject_b',
-      status: 'rejected'
+      status: 'rejected',
+      description:
+        'This is some description information about the pending task, if there is too much content.'
     })
   } else if (status === 'Cancelled') {
     stepList.value.push({
       label: 'Cancelled',
-      date: 'Jun-05-2024',
+      date: data.update_time || '--',
       icon: 'icon_cancelled_b',
-      status: 'completed'
+      status: 'cancelled'
     })
   }
 }
-const deliveryInfoData = ref({
-  modeType: '',
-  deliveryDate: '',
-  deliveryAddress: '',
-  specialRequirements: ''
-})
 
 const clearData = () => {
   shipmentInfoTableData.value = []
   stepList.value = []
-  deliveryInfoData.value = {
-    modeType: '',
-    deliveryDate: '',
-    deliveryAddress: '',
-    specialRequirements: ''
-  }
+  rowData.value = {}
 }
 defineExpose({
   openDialog
@@ -121,10 +110,10 @@ defineExpose({
       <DetailStep :stepList="stepList" />
       <div class="booking-info">
         <div class="booking-no">
-          <span class="no">Booking No.B83131200164</span>
-          <v-tag class="tag" type="Pending Approval">Pending Approval</v-tag>
+          <span class="no">Booking No.{{ rowData?.booking_no }}</span>
+          <v-tag class="tag" :type="rowData?.status">{{ rowData?.status }}</v-tag>
         </div>
-        <div class="created-time">Created Time:Jun-01-2024</div>
+        <div class="created-time">{{ formatTimezone(rowData?.created_time) }}</div>
       </div>
       <ShipmentInforTable :data="shipmentInfoTableData" />
       <div class="delivery-information">
@@ -132,35 +121,33 @@ defineExpose({
         <div class="delivery-info">
           <div class="delivery-item inline-flex" style="width: 200px">
             <span class="item-label">Mode Type</span>
-            <span class="item-value">{{ deliveryInfoData.modeType }}</span>
+            <span class="item-value">{{ rowData?.delivery_mode || '--' }}</span>
           </div>
           <div class="delivery-item inline-flex">
             <span class="item-label">Delivery Date</span>
             <span class="item-value">
               <span class="font_family icon-icon_date_b" style="margin-right: 4px"></span>
-              <span style="margin-top: 1px">{{
-                formatTimezone(deliveryInfoData.deliveryDate)
-              }}</span>
+              <span style="margin-top: 1px">{{ formatTimezone(rowData?.delivery_date) }}</span>
             </span>
           </div>
           <div class="delivery-item">
             <span class="item-label">Delivery Address</span>
             <span class="item-value">
               <span class="font_family icon-icon_location_b" style="margin-right: 2px"></span>
-              <span style="margin-top: 1px">{{ deliveryInfoData.deliveryAddress }}</span>
+              <span style="margin-top: 1px">{{ rowData?.delivery_address || '--' }}</span>
             </span>
           </div>
           <div class="delivery-item">
             <span class="item-label">Special Requirements</span>
             <span class="item-value">
               <span class="font_family icon-icon_paragraph_b" style="margin-right: 2px"></span>
-              <span style="margin-top: 1px">{{ deliveryInfoData.specialRequirements }}</span>
+              <span style="margin-top: 1px">{{ rowData?.special_requirements || '--' }}</span>
             </span>
           </div>
         </div>
       </div>
       <el-divider style="margin-top: 8px; margin-bottom: 20px" />
-      <OperationLogProcess />
+      <OperationLogProcess :processList="processList" />
     </div>
   </el-dialog>
 </template>

+ 9 - 4
src/views/DestinationDelivery/src/components/TableView/src/components/DetailStep.vue

@@ -6,7 +6,7 @@ const props = defineProps<{
     label: string
     date?: string
     icon: string
-    status: 'completed' | 'current' | 'unfinished' | 'rejected' | 'approved'
+    status: 'completed' | 'current' | 'unfinished' | 'rejected' | 'approved' | 'cancelled'
     description?: string
   }>
 }>()
@@ -20,12 +20,11 @@ const props = defineProps<{
         <div
           class="step-icon"
           :class="{
+            'step-icon-approved': step.label === 'Approved' && step.status !== 'unfinished',
             'step-icon-unfinished': step.status === 'unfinished',
             'step-icon-current': step.status === 'current',
             'step-icon-rejected': step.status === 'rejected',
-            'step-icon-approved':
-              step.label === 'Approved' &&
-              (step.status === 'completed' || step.status === 'current')
+            'step-icon-cancelled': step.status === 'cancelled'
           }"
         >
           <span
@@ -127,6 +126,12 @@ const props = defineProps<{
       color: #5bb462;
     }
   }
+  &.step-icon-cancelled {
+    background: var(--color-steps-cancelled-bg);
+    span {
+      color: var(--color-steps-cancelled);
+    }
+  }
 }
 
 .step-text {

+ 37 - 20
src/views/DestinationDelivery/src/components/TableView/src/components/EmailDialog.vue

@@ -7,8 +7,20 @@ import { formatTimezone } from '@/utils/tools'
 i18nChangeLanguage('en')
 
 const visible = ref(false)
+const rowData = ref()
 const openDialog = (row) => {
+  emailData.value.email = row.kln_pic
+  $api
+    .getEmailRecords({
+      serial_no: row._serial_no
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        emailRecords.value = res.data.emailRecords
+      }
+    })
   visible.value = true
+  rowData.value = row
 }
 const props = defineProps({
   data: Object
@@ -16,17 +28,10 @@ const props = defineProps({
 
 const emailData = ref({
   email: '',
-  ccEmail: '',
-  serial_no: ''
+  ccEmail: ''
 })
 
-const emailRecords: any = ref([
-  {
-    name: 'John Doe',
-    content: 'This is a test email content.',
-    creatTime: '2024-06-01T16:25:31Z'
-  }
-])
+const emailRecords: any = ref([])
 watch(
   () => props.data,
   (newVal) => {
@@ -34,7 +39,6 @@ watch(
       const email = newVal?.email
       emailData.value.email = email?.email
       emailData.value.ccEmail = email?.cc_email
-      emailData.value.serial_no = newVal?.serial_no
       emailRecords.value = email?.emailRecords
     }
   },
@@ -151,9 +155,10 @@ const sendEmail = () => {
     .saveDliveryBookingEmail({
       email: emailData.value.email,
       communication_cc: emailData.value.ccEmail,
-      serial_no: emailData.value.serial_no,
+      serial_no: rowData.value._serial_no,
       content: html,
-      text
+      text,
+      h_bol: rowData.value.h_bol
     })
     .then((res: any) => {
       if (res.code === 200) {
@@ -166,6 +171,15 @@ const sendEmail = () => {
     })
 }
 
+const clearData = () => {
+  valueHtml.value = ''
+  emailData.value = {
+    email: '',
+    ccEmail: ''
+  }
+  emailRecords.value = []
+}
+
 defineExpose({
   openDialog
 })
@@ -174,11 +188,12 @@ defineExpose({
 <template>
   <el-dialog
     title="Communication"
-    class="booking-detail-email-dialog"
+    class="delivery-email-dialog"
     v-model="visible"
     :close-on-click-modal="false"
     width="1000px"
-    top="15vh"
+    top="12vh"
+    @closed="clearData"
     v-if="visible"
   >
     <div class="email-view">
@@ -259,16 +274,10 @@ defineExpose({
 .email-view {
   display: flex;
   flex-direction: column;
-
   padding: 16px;
   padding-bottom: 0;
   border-radius: 12px;
   background: var(--color-email-bg);
-
-  .show-records {
-    max-height: 370px;
-    overflow: auto;
-  }
 }
 
 :deep(.w-e-text-container) {
@@ -373,3 +382,11 @@ defineExpose({
   }
 }
 </style>
+<style>
+.delivery-email-dialog {
+  .show-records {
+    max-height: 300px;
+    overflow: auto;
+  }
+}
+</style>

+ 26 - 17
src/views/DestinationDelivery/src/components/TableView/src/components/OperationLogProcess.vue

@@ -1,20 +1,29 @@
 <script setup lang="ts">
-const processList = ref([
+import { formatTimezone } from '@/utils/tools'
+const props = withDefaults(
+  defineProps<{
+    processList: Array<any>
+  }>(),
   {
-    time: 'Jun-01-2024 16:25:31 ',
-    timezone: 'UTC+3',
-    label: 'Submit Booking',
-    createdBy: 'Customer A',
-    tips: '--'
-  },
-  {
-    time: 'May-15-2024 16:25:31 ',
-    timezone: 'UTC+3',
-    label: 'Reject Booking',
-    createdBy: 'John Doe',
-    tips: 'Too early'
+    processList: []
   }
-])
+)
+// const processList = ref([
+//   {
+//     time: 'Jun-01-2024 16:25:31 ',
+//     timezone: 'UTC+3',
+//     label: 'Submit Booking',
+//     createdBy: 'Customer A',
+//     tips: '--'
+//   },
+//   {
+//     time: 'May-15-2024 16:25:31 ',
+//     timezone: 'UTC+3',
+//     label: 'Reject Booking',
+//     createdBy: 'John Doe',
+//     tips: 'Too early'
+//   }
+// ])
 </script>
 
 <template>
@@ -29,11 +38,11 @@ const processList = ref([
             <div class="process-line" v-if="index !== processList.length - 1"></div>
           </div>
           <div class="process-content">
-            <p class="process-time">{{ item.time }}</p>
+            <p class="process-time">{{ formatTimezone(item.time, item.timezone) }}</p>
             <div class="process-data">
               <div class="process-data-label">{{ item.label }}</div>
               <div class="process-data-user">{{ item.createdBy }}</div>
-              <div class="--process-data-tips">{{ item.tips }}</div>
+              <div class="--process-data-tips">{{ item.tips || '--' }}</div>
             </div>
           </div>
         </div>
@@ -118,7 +127,7 @@ const processList = ref([
               padding: 0 8px;
               border-radius: 6px;
               line-height: 28px;
-              background: var(--color---process-data-tips-bg);
+              background: var(--color--process-data-tips-bg);
             }
           }
         }

+ 7 - 1
src/views/DestinationDelivery/src/components/TableView/src/components/ShipmentInforTable.vue

@@ -36,7 +36,13 @@ const tableData = ref<VxeGridProps<any>>({
       field: 'Recommended Delivery Date',
       title: 'Recommended Delivery Date',
       width: 220,
-      formatter: ({ cellValue }: any) => formatTimezone(cellValue)
+      formatter: ({ cellValue }: any) => {
+        if (!cellValue) return '--'
+        const rangeData = cellValue.split(';')
+        const leftDate = rangeData[0] ? formatTimezone(rangeData[0]) : '_'
+        const rightDate = rangeData[1] ? formatTimezone(rangeData[0]) : '_'
+        return leftDate + ' -- ' + rightDate
+      }
     }
   ],
   data: [],

+ 56 - 21
src/views/DestinationDelivery/src/components/TableView/src/components/TipsDialog.vue

@@ -1,28 +1,60 @@
 <script setup lang="ts">
-const props = defineProps<{
-  type: 'approve' | 'reject' | 'cancel'
-  bookingNo: string
-}>()
-const model = defineModel<boolean>({ type: Boolean, default: false })
-
+const dialogType = ref()
+const bookingNo = ref()
+const dialogVisible = ref(false)
+const serialNo = ref()
 const tipList = ref({
   reject: 'Are you sure you want to Reject Booking ',
   approve: 'Are you sure you want to Approve Booking ',
   cancel: 'Are you sure you want to Cancel Booking '
 })
+const notes = ref()
 
-const typeList = ref({
+const typeList = {
   approve: 'Approve',
   reject: 'Reject',
   cancel: 'Cancel'
-})
+}
 
-const isShowRequired = computed(() => {
-  return props.type === 'reject'
-})
-const openDialog = () => {
-  model.value = true
+const openDialog = (type: string, row: any) => {
+  dialogVisible.value = true
+  bookingNo.value = row.booking_no
+  dialogType.value = type
+  serialNo.value = row._serial_no
 }
+const emit = defineEmits(['stateChange'])
+
+const handleSubmit = () => {
+  if (dialogType.value === 'reject' && !notes.value) {
+    ElMessage.warning('A remark must be filled in for the rejection operation.')
+    return
+  }
+  $api
+    .reviewDliveryBooking({
+      serial_no: serialNo.value,
+      status: typeList[dialogType.value],
+      notes: notes.value
+    })
+    .then((res) => {
+      if (res.code === 200) {
+        dialogVisible.value = false
+        ElMessage.success(`${typeList[dialogType.value]} successfully`)
+        emit('stateChange')
+      } else {
+        ElMessage.error(`${typeList[dialogType.value]} failed, Please try again.`)
+      }
+    })
+    .catch(() => {
+      ElMessage.error(`${typeList[dialogType.value]} failed, Please try again.`)
+    })
+}
+const clearData = () => {
+  bookingNo.value = ''
+  dialogType.value = ''
+  serialNo.value = ''
+  notes.value = ''
+}
+
 defineExpose({
   openDialog
 })
@@ -30,35 +62,38 @@ defineExpose({
 
 <template>
   <el-dialog
-    v-model="model"
+    v-model="dialogVisible"
     :show-close="false"
     title=""
     width="800px"
+    @closed="clearData"
     top="20vh"
     class="tips-dialog"
   >
     <div class="tips-dialog-title">
       <span style="" class="font_family icon-icon_tipsfilled_b"></span>
-      <span style="color: #c9353f" v-if="isShowRequired">*</span>
-      <span> {{ tipList[props.type] + props.bookingNo + '?' }}</span>
+      <span style="color: #c9353f" v-if="dialogType === 'reject'">*</span>
+      <span> {{ tipList[dialogType] + bookingNo + '?' }}</span>
     </div>
     <el-input
       :autosize="false"
       type="textarea"
+      v-model="notes"
       class="input-textarea"
       style="height: 120px"
       placeholder="Input remarks"
     ></el-input>
     <template #footer>
-      <el-button class="cancel-btn" type="default" @click="model = false">Cancel</el-button>
+      <el-button class="cancel-btn" type="default" @click="dialogVisible = false">Cancel</el-button>
       <el-button
         class="submit-btn"
         :class="{
-          'el-button--success': props.type === 'approve',
-          'el-button--danger': props.type === 'reject',
-          'el-button--warning': props.type === 'cancel'
+          'el-button--success': dialogType === 'approve',
+          'el-button--danger': dialogType === 'reject',
+          'el-button--warning': dialogType === 'cancel'
         }"
-        >{{ typeList[props.type] }} Booking</el-button
+        @click="handleSubmit"
+        >{{ typeList[dialogType] }} Booking</el-button
       >
     </template>
   </el-dialog>

+ 1 - 1
src/views/Tracking/src/components/TrackingDetail/src/components/AddReferenceDialog.vue

@@ -39,7 +39,7 @@ const tableData = ref<VxeGridProps<any>>({
     {
       title: 'Action',
       width: 90,
-      fixed: 'right',
+      fixed: 'left',
       slots: { default: 'action' }
     }
   ],

+ 1 - 0
src/views/Tracking/src/components/TrackingDetail/src/components/AttachmentView.vue

@@ -54,6 +54,7 @@ watch(
         {
           title: 'Action',
           width: 90,
+          fixed: 'left',
           slots: { default: 'action' }
         }
       ]

+ 25 - 21
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -92,7 +92,7 @@ const getTableColumns = async () => {
       if (index !== -1) {
         trackingTable.value.columns.push({
           title: 'Action',
-          fixed: 'right',
+          fixed: 'left',
           width: 120,
           slots: { default: 'action' }
         })
@@ -136,7 +136,7 @@ const assignTableData = (data: any) => {
     if (index === -1) {
       trackingTable.value.columns.push({
         title: 'Action',
-        fixed: 'right',
+        fixed: 'left',
         width: 120,
         slots: { default: 'action' }
       })
@@ -275,13 +275,14 @@ const trackingTable = ref<any>({
   headerRowStyle: {
     backgroundColor: 'var(--color-table-header-bg)'
   },
-  cellStyle: ({ column }) => {
-    if (column.title === 'Action' && themeStore.theme === 'dark') {
-      return {
-        backgroundColor: '#363940'
-      }
-    }
-  },
+  // action栏特殊的样式
+  // cellStyle: ({ column }) => {
+  //   if (column.title === 'Action' && themeStore.theme === 'dark') {
+  //     return {
+  //       backgroundColor: '#363940'
+  //     }
+  //   }
+  // },
   menuConfig: {
     body: {
       options: [
@@ -638,18 +639,21 @@ defineExpose({
       </template>
       <!-- action操作的插槽 -->
       <template #action="{ row }">
-        <el-button class="recent_button el-button--blue" @click="SubscribeShipments(row)">
-          <span v-if="row.is_subscribe" style="color: 'red'" class="iconfont_icon">
-            <svg class="iconfont" aria-hidden="true" style="fill: var(--color-theme)">
-              <use xlink:href="#icon-icon_marked_b"></use>
-            </svg>
-          </span>
-          <span v-else class="iconfont_icon" style="color: 'red'">
-            <svg class="iconfont" aria-hidden="true" style="color: 'red'">
-              <use xlink:href="#icon-icon_unmark_b"></use>
-            </svg>
-          </span>
-        </el-button>
+        <el-tooltip class="box-item" effect="dark" content="Subscribe" placement="top">
+          <el-button class="recent_button el-button--blue" @click="SubscribeShipments(row)">
+            <span v-if="row.is_subscribe" style="color: 'red'" class="iconfont_icon">
+              <svg class="iconfont" aria-hidden="true" style="fill: var(--color-theme)">
+                <use xlink:href="#icon-icon_marked_b"></use>
+              </svg>
+            </span>
+            <span v-else class="iconfont_icon" style="color: 'red'">
+              <svg class="iconfont" aria-hidden="true" style="color: 'red'">
+                <use xlink:href="#icon-icon_unmark_b"></use>
+              </svg>
+            </span>
+          </el-button>
+        </el-tooltip>
+
         <el-button
           v-if="row?.['Mode'] !== 'Air Freight' && canEdiVgm"
           @click="handleVGM(row)"