Преглед изворни кода

feat: create new booking加上delivery date筛选项

Jack Zhou пре 2 недеља
родитељ
комит
5182f011a7

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

@@ -318,4 +318,19 @@ export const getEmailRecords = (params: any, config: any) => {
     },
     config
   )
+}
+
+/**
+ * create new booking 表格中 Packing List 和 Commercial Invoice 列的下载功能
+ */
+export const downloadBookingTableFile = (params: any, config: any) => {
+  return HttpAxios.post(
+    `${baseUrl}`,
+    {
+      action: 'destination_delivery_booking',
+      operate: 'batch_download_ci_p_file',
+      ...params
+    },
+    config
+  )
 }

+ 5 - 1
src/utils/table.ts

@@ -6,7 +6,7 @@ import { type VxeGridInstance, type VxeGridProps } from 'vxe-table'
  * @param grid 表格实例
  * @returns
  */
-export const autoWidth = (tableData: VxeGridProps, grid: VxeGridInstance) => {
+export const autoWidth = (tableData: VxeGridProps, grid: VxeGridInstance, customizeWidth?: { [key: string]: number }) => {
   const columns = tableData.columns
   const data = tableData.data
   const columnsWidth: { width: number; field: any }[] = []
@@ -47,6 +47,10 @@ export const autoWidth = (tableData: VxeGridProps, grid: VxeGridInstance) => {
       if (field === 'Mode') {
         width = 80
       }
+      // 如果有自定义宽度,则使用自定义宽度
+      if (customizeWidth && customizeWidth[field]) {
+        width = customizeWidth[field]
+      }
 
       columnsWidth.push({
         width,

+ 77 - 50
src/views/DestinationDelivery/src/components/CreateNewBooking/src/CreateNewbooking.vue

@@ -27,13 +27,15 @@ const VesselName = ref([])
 const VesselNametest = ref('')
 const ShipperValue = ref('')
 const ConsigneeValue = ref('')
+const deliveryDate = ref('')
 const DeliveryReference = ref('')
 const getAddressListData = ref({})
 // const isFocused = ref(false)
 const isFocused = ref({
   Shipper: false,
   Consignee: false,
-  Vessel: false
+  Vessel: false,
+  deliveryDate: false
 })
 const isataFocused = ref(false)
 const isetaFocused = ref(false)
@@ -198,10 +200,6 @@ const showETAlabel = computed(() => {
   return ETATimeList.value != null || isetaFocused.value
 })
 
-const changeFocus = (val: boolean) => {
-  // isFocused.value = val
-}
-
 const changeFocustest = (type: any, val: boolean) => {
   isFocused.value[type] = val
 }
@@ -227,34 +225,34 @@ const querySearchCountry = (query: string) => {
   Countryloading.value = true
   setTimeout(() => {
     $api
-    .getAddressCountryCityData({
-      term: query,
-      term_type: 'country',
-      limit: CityCode.value != ''  ? CityCode.value : ''
-    })
-    .then((res: any) => {
-      if (res.code === 200) {
-        Countryoptions.value = res.data
-        Countryloading.value = false
-      }
-    })
+      .getAddressCountryCityData({
+        term: query,
+        term_type: 'country',
+        limit: CityCode.value != '' ? CityCode.value : ''
+      })
+      .then((res: any) => {
+        if (res.code === 200) {
+          Countryoptions.value = res.data
+          Countryloading.value = false
+        }
+      })
   }, 1000)
 }
 const querySearchCity = (query: string) => {
   cityloading.value = true
   setTimeout(() => {
     $api
-    .getAddressCountryCityData({
-      term: query,
-      term_type: 'city',
-      limit: CountryCode.value != ''  ? CountryCode.value : ''
-    })
-    .then((res: any) => {
-      if (res.code === 200) {
-        Cityoptions.value = res.data
-        cityloading.value = false
-      }
-    })
+      .getAddressCountryCityData({
+        term: query,
+        term_type: 'city',
+        limit: CountryCode.value != '' ? CountryCode.value : ''
+      })
+      .then((res: any) => {
+        if (res.code === 200) {
+          Cityoptions.value = res.data
+          cityloading.value = false
+        }
+      })
   }, 1000)
 }
 // 特殊日期样式
@@ -310,12 +308,12 @@ const AddNewAddressDelivery = () => {
 // 保存新地址
 const SaveNewAddress = () => {
   if (
-    CountryCode.value != '' &&
-    CityCode.value != '' &&
-    PostalCode.value != '' &&
-    ContactPerson.value != '' &&
-    ContactNumber.value != '' &&
-    AddressLine1.value != '' ||
+    (CountryCode.value != '' &&
+      CityCode.value != '' &&
+      PostalCode.value != '' &&
+      ContactPerson.value != '' &&
+      ContactNumber.value != '' &&
+      AddressLine1.value != '') ||
     AddressLine2.value != '' ||
     AddressLine3.value != '' ||
     AddressLine4.value != ''
@@ -464,6 +462,7 @@ const SearchShipment = () => {
     vessel: VesselNametest.value,
     consignee: ConsigneeValue.value,
     shipper: ShipperValue.value,
+    recommended_delivery_date: deliveryDate.value,
     eta_start: ETATimeList.value != null ? ETATimeList.value[0] : '',
     eta_end: ETATimeList.value != null ? ETATimeList.value[1] : '',
     ata_start: ATATimeList.value != null ? ATATimeList.value[0] : '',
@@ -616,7 +615,6 @@ const handleClickEditAddress = (val: any) => {
 // 删除地址
 const handleDeleteAddress = (val: any) => {
   const key = val.contact_type == 'Unedit' ? 'sync_key' : 'id'
-  console.log(val.contact_type)
   if (val.contact_type !== 'Add') {
     updateAddressList({ ...val, contact_type: 'Delete' })
   }
@@ -730,11 +728,10 @@ onMounted(() => {
         >
       </div>
       <div class="shipments_search" v-if="a == undefined">
-        <div class="flex">
+        <div class="left-filter-search">
           <el-input
             placeholder="Enter Booking/HBL/PO/Carrier Booking No. "
             v-model="CreateNewBOokingSearch"
-            style="width: 34%"
             class="log_input"
           >
             <template #prefix>
@@ -769,14 +766,7 @@ onMounted(() => {
             </el-input>
             <span v-if="showLabelConsignee" class="border-label">Consignee</span>
           </div>
-          <el-button
-            style="width: 108px"
-            class="el-button--dark create-button"
-            @click="SearchShipment"
-            >Search</el-button
-          >
-        </div>
-        <div class="flex" style="margin-top: 8px">
+
           <div class="input-with-label">
             <CalendarDate
               :isNeedFooter="false"
@@ -796,7 +786,26 @@ onMounted(() => {
             ></CalendarDate>
             <span v-if="showataLabel" class="border-label">ATA</span>
           </div>
+
           <div class="input-with-label" style="margin-right: 8px">
+            <!-- <AutoSelect ASPlaceholder="Input Vessel Name" :ASValue="VesselName" @changeFocus="changeFocus"></AutoSelect> -->
+            <a-date-picker
+              v-model:value="deliveryDate"
+              :showToday="false"
+              @focus="isFocused.deliveryDate = true"
+              @blur="isFocused.deliveryDate = false"
+              :format="userStore.dateFormat"
+              placeholder="Recommended Delivery Date"
+              valueFormat="MM/DD/YYYY"
+              style="width: 100%; height: 40px"
+            >
+            </a-date-picker>
+            <span v-if="isFocused.deliveryDate" class="border-label"
+              >Recommended Delivery Date</span
+            >
+          </div>
+
+          <div class="input-with-label">
             <!-- <AutoSelect ASPlaceholder="Input Vessel Name" :ASValue="VesselName" @changeFocus="changeFocus"></AutoSelect> -->
             <el-input
               placeholder="Input Vessel Name"
@@ -804,11 +813,19 @@ onMounted(() => {
               @focus="changeFocustest('Vessel', true)"
               @blur="changeFocustest('Vessel', false)"
               class="log_input"
+              style="width: 100%"
             >
             </el-input>
             <span v-if="showLabelVessel" class="border-label">Vessel Name</span>
           </div>
-          <div style="width: 108px"></div>
+        </div>
+        <div class="right-btn">
+          <el-button
+            style="width: 108px"
+            class="el-button--dark create-button"
+            @click="SearchShipment"
+            >Search</el-button
+          >
         </div>
       </div>
       <NewbookingTable
@@ -937,9 +954,14 @@ onMounted(() => {
             placeholder="Please Select Time"
           ></el-time-select>
         </div>
-        <div style="margin-left: 12px;">
+        <div style="margin-left: 12px">
           <div class="delivery_type_title">Delivery Reference</div>
-          <el-tooltip class="item" effect="dark" content="Reference to be quoted on arrival at the Warehouse/DC" placement="bottom">
+          <el-tooltip
+            class="item"
+            effect="dark"
+            content="Reference to be quoted on arrival at the Warehouse/DC"
+            placement="bottom"
+          >
             <el-input v-model="DeliveryReference" class="delivery_reference"></el-input>
           </el-tooltip>
         </div>
@@ -1431,7 +1453,14 @@ onMounted(() => {
   font-weight: 400;
 }
 .shipments_search {
-  margin: 0 0 8px 0;
+  display: flex;
+  margin-bottom: 16px;
+}
+.left-filter-search {
+  flex: 1;
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  grid-gap: 8px;
 }
 :deep(.log_input .el-input__wrapper) {
   box-shadow: 0 0 0 1px var(--color-select-border) inset;
@@ -1439,8 +1468,6 @@ onMounted(() => {
 }
 .input-with-label {
   position: relative;
-  display: inline-block;
-  width: 34%;
 }
 .border-label {
   position: absolute;

+ 59 - 6
src/views/DestinationDelivery/src/components/CreateNewBooking/src/components/NewbookingTable.vue

@@ -4,6 +4,7 @@ import { useRowClickStyle } from '@/hooks/rowClickStyle'
 import { formatTimezone } from '@/utils/tools'
 import { ref, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
+import { autoWidth } from '@/utils/table'
 
 const router = useRouter()
 const { currentRoute } = router
@@ -62,7 +63,6 @@ const handleColumns = (columns: any) => {
         }
       }
     } else if (item.type === 'download') {
-      console.log('download column found')
       curColumn = {
         ...curColumn,
         slots: { default: 'download' }
@@ -89,6 +89,11 @@ const getTableColumns = async () => {
   })
   nextTick(() => {
     tableLoadingColumn.value = false
+    tableRef.value &&
+      autoWidth(tableData.value, tableRef.value, {
+        packing_list: 190,
+        commercial_invoice: 180
+      })
   })
 }
 // 获取表格数据
@@ -109,6 +114,15 @@ const searchTableData = (val: any) => {
         tableData.value.data = res.data.data
       }
     })
+    .finally(() => {
+      nextTick(() => {
+        tableRef.value &&
+          autoWidth(tableData.value, tableRef.value, {
+            packing_list: 190,
+            commercial_invoice: 180
+          })
+      })
+    })
 }
 
 const emits = defineEmits(['selectChangeEvent'])
@@ -217,7 +231,49 @@ const selectAllChangeEvent = () => {
   }
 }
 
-const handleDownload = (row: any) => {}
+function base64ToBlob(base64, mimeType) {
+  const byteString = atob(base64.split(',')[0].startsWith('data:') ? base64.split(',')[1] : base64)
+  const ab = new ArrayBuffer(byteString.length)
+  const ia = new Uint8Array(ab)
+  for (let i = 0; i < byteString.length; i++) {
+    ia[i] = byteString.charCodeAt(i)
+  }
+  return new Blob([ia], { type: mimeType })
+}
+
+const handleDownload = (serialNo: string, field: string) => {
+  const fileType = field === 'commercial_invoice' ? 'C/I' : 'Packing List'
+  $api
+    .downloadBookingTableFile({
+      serial_no: serialNo,
+      file_type: fileType
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        // 使用
+        const base64 = res.data.data // 纯 base64 字符串,不含 data: 前缀
+        const mimeType = 'application/octet-stream'
+        const fileName = res.data.filename || 'download'
+
+        const blob = base64ToBlob(base64, mimeType)
+        const url = URL.createObjectURL(blob)
+
+        const link = document.createElement('a')
+        link.href = url
+        link.download = fileName
+        document.body.appendChild(link)
+        link.click()
+        document.body.removeChild(link)
+
+        // 可选:下载完成后释放内存
+        link.onclick = () => {
+          setTimeout(() => {
+            URL.revokeObjectURL(url) // 释放对象 URL
+          }, 100)
+        }
+      }
+    })
+}
 
 // 实现行点击样式
 useRowClickStyle(tableRef)
@@ -243,7 +299,7 @@ defineExpose({
     >
       <!-- download下载的插槽 -->
       <template #download="{ row, column }">
-        <div class="download-btn">
+        <div class="download-btn" @click="handleDownload(row.serial_no, column.field)">
           <span class="font_family icon-icon_download_b icon-style"> </span>
           <span
             >{{ row.h_bol
@@ -262,9 +318,6 @@ defineExpose({
 </template>
 
 <style lang="scss" scoped>
-.font_family::before {
-  color: var(--color-btn-danger-bg);
-}
 .icon_alert::before {
   color: var(--color-btn-warning-bg);
 }

+ 1 - 1
src/views/Tracking/src/components/DownloadAttachment/src/DownloadAttachment.vue

@@ -227,7 +227,7 @@ const handleDownloadAllSelectedFiles = (label?: string) => {
         })
         return
       } else if (res.data?.code === 500) {
-        ElMessageBox.alert(res.data.message, 'Prompt', {
+        ElMessageBox.alert(res.data.msg, 'Prompt', {
           confirmButtonText: 'OK',
           confirmButtonClass: 'el-button--dark'
         })