|
|
@@ -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;
|