Przeglądaj źródła

feat: 完善Tracking页下载功能页面

Jack Zhou 2 miesięcy temu
rodzic
commit
96d7a83156

+ 1 - 1
src/styles/theme.scss

@@ -584,6 +584,6 @@
 
   --color-json-item-hover: #3e5966;
 
-  --color-attchment-summary-bg: #f9fafb;
+  --color-attchment-summary-bg: #2b2f36;
 }
   

+ 145 - 50
src/views/Tracking/src/components/DownloadAttachment/src/DownloadAttachment.vue

@@ -1,107 +1,179 @@
 <script setup lang="ts">
-const selectAll = ref(false)
-
-const attachmentData = [
+const attachmentData = ref([
   {
     id: 1,
+    isSelect: false,
     no: 'Shipment No. S0000002841',
     typeList: [
       {
         label: 'Customs Documents',
         number: 2,
         attachmentList: [
-          { name: 'Commercial Invoice.pdf' },
-          { name: 'Packing List.pdf' },
-          { name: 'Certificate of Origin.pdf' }
+          { name: 'Commercial Invoice.pdf', isSelect: false },
+          { name: 'Packing List.pdf', isSelect: false },
+          { name: 'Certificate of Origin.pdf', isSelect: false }
         ]
       },
       {
         label: 'House Bill of Lading',
         number: 1,
-        attachmentList: [{ name: 'Commercial Invoice.pdf' }]
+        attachmentList: [{ name: 'Commercial Invoice.pdf', isSelect: false }]
       },
       {
         label: 'Master Bill of Lading',
         number: 1,
-        attachmentList: [{ name: 'Commercial Invoice.pdf' }]
+        attachmentList: [{ name: 'Commercial Invoice.pdf', isSelect: false }]
       }
     ]
   },
   {
     id: 2,
+    isSelect: false,
     no: 'Shipment No. S0000002841',
     typeList: [
       {
         label: 'Customs Documents',
         number: 2,
         attachmentList: [
-          { name: 'Commercial Invoice.pdf' },
-          { name: 'Packing List.pdf' },
-          { name: 'Certificate of Origin.pdf' }
+          { name: 'Commercial Invoice.pdf', isSelect: false },
+          { name: 'Packing List.pdf', isSelect: false },
+          { name: 'Certificate of Origin.pdf', isSelect: false }
         ]
       },
       {
         label: 'House Bill of Lading',
         number: 1,
-        attachmentList: [{ name: 'Commercial Invoice.pdf' }]
+        attachmentList: [{ name: 'Commercial Invoice.pdf', isSelect: false }]
       },
       {
         label: 'Master Bill of Lading',
         number: 1,
-        attachmentList: [{ name: 'Commercial Invoice.pdf' }]
+        attachmentList: [{ name: 'Commercial Invoice.pdf', isSelect: false }]
       }
     ]
   },
   {
     id: 3,
+    isSelect: false,
     no: 'Shipment No. S0000002841',
     typeList: []
   },
   {
     id: 2,
     no: 'Shipment No. S0000002841',
+    isSelect: false,
     typeList: [
       {
         label: 'Customs Documents',
         number: 2,
         attachmentList: [
-          { name: 'Commercial Invoice.pdf' },
-          { name: 'Packing List.pdf' },
-          { name: 'Certificate of Origin.pdf' }
+          { name: 'Commercial Invoice.pdf', isSelect: false },
+          { name: 'Packing List.pdf', isSelect: false },
+          { name: 'Certificate of Origin.pdf', isSelect: false }
         ]
       }
     ]
   }
-]
+])
+
+// const shipments = ref(attachmentData)
+
+// === 1. 全选状态计算 ===
+const isAllSelected = computed({
+  get() {
+    return attachmentData.value.every((item) => item.isSelect || item.typeList?.length === 0)
+  },
+  set(val) {
+    attachmentData.value.forEach((item) => {
+      if (item.typeList?.length === 0) return
+      item.isSelect = val
+      // 同步子级
+      if (item.typeList) {
+        item.typeList.forEach((type) => {
+          if (type.attachmentList) {
+            type.attachmentList.forEach((att) => {
+              att.isSelect = val
+            })
+          }
+        })
+      }
+    })
+  }
+})
+
+const handleParentToggle = (ship) => {
+  const newVal = ship.isSelect
+  ship.typeList.forEach((type) => {
+    if (type.attachmentList) {
+      type.attachmentList.forEach((att) => {
+        att.isSelect = newVal
+      })
+    }
+  })
+}
 
-// const summaryList = [
-//   {
-//     label: 'Customs Documents',
-//     number: 8,
-//     attachmentList: [
-//       {
-//         name: 'Commercial InvoiceCommercial InvoiceCommercial InvoiceCommercial Invoice.pdf'
-//       },
-//       {
-//         name: 'Packing List.pdf'
-//       },
-//       {
-//         name: 'Certificate of Origin.pdf'
-//       }
-//     ]
-//   },
-//   {
-//     label: 'House Bill of Lading',
-//     number: 4,
-//     attachmentList: [{ name: 'Commercial Invoice.pdf' }]
-//   },
-//   {
-//     label: 'Master Bill of Lading',
-//     number: 4,
-//     attachmentList: [{ name: 'Commercial Invoice.pdf' }]
-//   }
-// ]
-const summaryList = []
+// 子级变化时,更新父级状态
+const handleChildToggle = (ship) => {
+  if (!ship.typeList || ship.typeList.length === 0) {
+    // 如果没有子项,直接返回当前状态或设为 false
+    ship.isSelect = false
+    return
+  }
+
+  // 判断所有 attachment 是否都选中
+  const allSelected = ship.typeList.every((type) =>
+    type.attachmentList?.every((att) => att.isSelect)
+  )
+
+  ship.isSelect = allSelected
+}
+
+// === 3. 初始化数据结构(确保每个 attachment 都有 isSelect)
+// 如果原始数据不完整,可以预处理
+const initShipments = () => {
+  attachmentData.value.forEach((item) => {
+    if (!item.isSelect) item.isSelect = false
+    if (item.typeList) {
+      item.typeList.forEach((type) => {
+        if (type.attachmentList) {
+          type.attachmentList.forEach((att) => {
+            if (!att.isSelect) att.isSelect = false
+          })
+        }
+      })
+    }
+  })
+}
+
+initShipments()
+const summaryList = [
+  {
+    label: 'Customs Documents',
+    number: 8,
+    attachmentList: [
+      {
+        name: 'Commercial InvoiceCommercial InvoiceCommercial InvoiceCommercial Invoice.pdf'
+      },
+      {
+        name: 'Packing List.pdf'
+      },
+      {
+        name: 'Certificate of Origin.pdf'
+      }
+    ]
+  },
+  {
+    label: 'House Bill of Lading',
+    number: 4,
+    attachmentList: [{ name: 'Commercial Invoice.pdf' }]
+  },
+  {
+    label: 'Master Bill of Lading',
+    number: 4,
+    attachmentList: [{ name: 'Commercial Invoice.pdf' }]
+  }
+]
+// const summaryList = []
 
 const activeNames = ref(['1'])
 </script>
@@ -110,12 +182,16 @@ const activeNames = ref(['1'])
   <div class="tracking-download-attachment">
     <div class="left-select-section">
       <div class="header-select-all">
-        <el-checkbox v-model="selectAll"><span>Select All</span></el-checkbox>
+        <el-checkbox v-model="isAllSelected"><span>Select All</span></el-checkbox>
       </div>
       <div class="attachment-list">
         <div class="attachment-item" v-for="attItem in attachmentData" :key="attItem.id">
           <div class="top-number">
-            <el-checkbox>
+            <el-checkbox
+              :disabled="!attItem?.typeList?.length"
+              @change="handleParentToggle(attItem)"
+              v-model="attItem.isSelect"
+            >
               <span class="font_family icon-icon_ocean_b"></span>
               <span class="label">Attachment {{ attItem.no }}</span></el-checkbox
             >
@@ -133,7 +209,7 @@ const activeNames = ref(['1'])
                   v-for="fileItem in typeItem.attachmentList"
                   :key="fileItem.name"
                 >
-                  <el-checkbox>
+                  <el-checkbox v-model="fileItem.isSelect" @change="handleChildToggle(attItem)">
                     <span>{{ fileItem.name }}</span></el-checkbox
                   >
                   <span class="font_family icon-icon_download_b"></span>
@@ -190,8 +266,8 @@ const activeNames = ref(['1'])
             </div>
           </el-collapse-item>
         </el-collapse>
-        <div class="empty-file-data">
-          <img src="./images/empty-img.png" alt="empty-data" v-if="!summaryList?.length" />
+        <div class="empty-file-data" v-if="!summaryList?.length">
+          <img src="./images/empty-img.png" alt="empty-data" />
         </div>
       </div>
     </div>
@@ -205,6 +281,24 @@ const activeNames = ref(['1'])
   padding-left: 24px;
   .left-select-section {
     flex: 1;
+    .header-select-all {
+      :deep(.el-checkbox__inner) {
+        &::after {
+          top: 1px;
+          left: 7px;
+          height: 14px;
+          width: 6px;
+        }
+      }
+    }
+  }
+  :deep(.el-checkbox__inner) {
+    &::after {
+      top: 1px;
+      left: 5px;
+      height: 9px;
+      width: 4px;
+    }
   }
   .right-summary-section {
     width: 340px;
@@ -355,6 +449,7 @@ const activeNames = ref(['1'])
       align-items: center;
       justify-content: center;
       height: 16px;
+      padding-top: 1px;
       padding-left: 5px;
       padding-right: 4px;
       min-width: 16px;

+ 110 - 10
src/views/Tracking/src/components/TrackingTable/src/TrackingTable.vue

@@ -375,6 +375,7 @@ onMounted(() => {
   tableRef.value && autoWidth(trackingTable.value, tableRef.value)
 })
 
+const upIcon = ref(false)
 const downloadDialogRef = ref()
 const handleDownload = () => {
   const curSelectedColumns: string[] = []
@@ -389,6 +390,19 @@ const handleDownload = () => {
     selectedNumber.value || pageInfo.value.total
   )
 }
+const handleDownloadAttachments = () => {
+  if (selectedNumber.value === 0) {
+    ElMessageBox.alert('Please select at least one record to download attachments', {
+      confirmButtonText: 'OK',
+      confirmButtonClass: 'el-button--dark',
+      customClass: 'tracking-table-download-alert-popup'
+    })
+    return
+  }
+  router.push({
+    name: 'Tracking Download Attachment'
+  })
+}
 
 const exportLoading = ref(false)
 // 获取导出表格数据
@@ -608,17 +622,44 @@ const testA = () => {
         <el-button class="el-button--main el-button--pain-theme" @click="testA"
           >Tracking Download Attachment</el-button
         >
-        <el-button
-          class="el-button--main el-button--pain-theme"
-          @click="handleDownload"
-          :style="{
-            paddingRight: themeStore.theme === 'dark' ? '13px' : '16px',
-            paddingLeft: themeStore.theme === 'dark' ? '13px' : '11px'
-          }"
+        <el-popover
+          trigger="click"
+          top="15vh"
+          class="box-item"
+          :width="226"
+          placement="bottom-start"
         >
-          <span style="margin-right: 7px" class="font_family icon-icon_download_b"></span>
-          Download
-        </el-button>
+          <template #reference>
+            <el-button
+              class="el-button--main el-button--pain-theme download-btn"
+              @click="upIcon = !upIcon"
+              :style="{
+                paddingRight: themeStore.theme === 'dark' ? '13px' : '16px',
+                paddingLeft: themeStore.theme === 'dark' ? '13px' : '11px'
+              }"
+            >
+              <span style="margin-right: 7px" class="font_family icon-icon_download_b"></span>
+              Download
+              <span
+                class="font_family icon-icon_up_b download-up-icon"
+                :class="{ 'rotate-icon': upIcon }"
+              ></span>
+            </el-button>
+          </template>
+          <template #default>
+            <div style="width: 226px; padding: 8px">
+              <div class="download-option-item" @click="handleDownload">
+                <span class="font_family icon-icon_download_b"></span>
+                <span>Download Shipment Details</span>
+              </div>
+              <div class="download-option-item" @click="handleDownloadAttachments">
+                <span class="font_family icon-icon_download__template_b"></span>
+                <span>Download Attachments</span>
+              </div>
+            </div>
+          </template>
+        </el-popover>
+
         <el-button style="padding-left: 10px" type="default" @click="handleCustomizeColumns">
           <span style="margin-right: 6px" class="font_family icon-icon_column_b"></span>
           Customize Columns
@@ -725,6 +766,29 @@ const testA = () => {
 </template>
 
 <style lang="scss" scoped>
+.download-option-item {
+  display: flex;
+  align-items: center;
+  padding: 6px 8px;
+  border-radius: 4px;
+  cursor: pointer;
+  &:hover {
+    background-color: var(--color-btn-action-bg-hover);
+    span {
+      color: var(--color-theme);
+    }
+  }
+  span {
+    &:first-child {
+      font-size: 16px;
+      margin-right: 4px;
+      line-height: 18px;
+    }
+    line-height: 22px;
+    color: var(--color-text-secondary);
+  }
+}
+
 .table-tools {
   position: relative;
   display: flex;
@@ -737,6 +801,23 @@ const testA = () => {
     font-weight: 700;
     line-height: 32px;
   }
+  .right-tools-btn {
+    // .download-btn {
+    //   &:hover {
+    //     .download-up-icon {
+    //       transform: rotate(360deg);
+    //     }
+    //   }
+    // }
+    .download-up-icon {
+      margin-left: 2px;
+      transition: all 0.5s ease;
+      transform: rotate(180deg);
+      &.rotate-icon {
+        transform: rotate(0deg);
+      }
+    }
+  }
 }
 
 .bottom-pagination {
@@ -791,3 +872,22 @@ const testA = () => {
   }
 }
 </style>
+<style lang="scss">
+.tracking-table-download-alert-popup {
+  width: 400px;
+
+  .el-message-box__header {
+    display: none;
+  }
+  .el-message-box__content {
+    padding-top: 9px;
+  }
+  div.el-message-box__btns {
+    border: none;
+    .el-button--dark {
+      width: 100px;
+      height: 40px;
+    }
+  }
+}
+</style>