|
|
@@ -0,0 +1,597 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import { useTrackingDownloadData } from '@/stores/modules/trackingDownloadData'
|
|
|
+import emitter from '@/utils/bus'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import { useUserStore } from '@/stores/modules/user'
|
|
|
+
|
|
|
+const userStore = useUserStore()
|
|
|
+const router = useRouter()
|
|
|
+const trackingDownloadData = useTrackingDownloadData()
|
|
|
+const attachmentData = ref([])
|
|
|
+
|
|
|
+// const shipments = ref(attachmentData)
|
|
|
+const getAttachmentData = () => {
|
|
|
+ $api
|
|
|
+ .getDownloadAttachmentData({
|
|
|
+ serial_no_arr: trackingDownloadData.serialNoArr,
|
|
|
+ schemas_arr: trackingDownloadData.schemasArr
|
|
|
+ })
|
|
|
+ .then((res: any) => {
|
|
|
+ if (res.code === 200) {
|
|
|
+ attachmentData.value = res.data
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ getAttachmentData()
|
|
|
+})
|
|
|
+
|
|
|
+// === 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 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 = ref([])
|
|
|
+const allChooseFiles = computed(() => {
|
|
|
+ return summaryList.value.reduce((acc, curr) => {
|
|
|
+ acc += curr.attachmentList.length
|
|
|
+ return acc
|
|
|
+ }, 0)
|
|
|
+})
|
|
|
+
|
|
|
+const generateSummary = () => {
|
|
|
+ const map = new Map() // 用 label 作为 key
|
|
|
+
|
|
|
+ attachmentData.value.forEach((item) => {
|
|
|
+ item?.typeList?.forEach((type) => {
|
|
|
+ // 遍历该类型下的所有附件
|
|
|
+ type?.attachmentList?.forEach((attach) => {
|
|
|
+ if (attach.isSelect) {
|
|
|
+ const label = type.label
|
|
|
+ if (!map.has(label)) {
|
|
|
+ map.set(label, {
|
|
|
+ label,
|
|
|
+ number: 0,
|
|
|
+ attachmentList: []
|
|
|
+ })
|
|
|
+ }
|
|
|
+ const group = map.get(label)
|
|
|
+ group.number += 1 // 每选中一个就 +1
|
|
|
+
|
|
|
+ group.attachmentList.push({ name: attach.name })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ // 转为数组
|
|
|
+ summaryList.value = Array.from(map.values())
|
|
|
+}
|
|
|
+
|
|
|
+// 👇 监听 attachmentData 中所有 isSelect 的变化
|
|
|
+watch(
|
|
|
+ () => {
|
|
|
+ // 创建一个扁平化的路径数组,用于监听所有 isSelect
|
|
|
+ return attachmentData.value.map((item) =>
|
|
|
+ item.typeList?.map((type) => type.attachmentList?.map((att) => att.isSelect))
|
|
|
+ )
|
|
|
+ },
|
|
|
+ () => {
|
|
|
+ generateSummary()
|
|
|
+ },
|
|
|
+ { deep: true }
|
|
|
+)
|
|
|
+
|
|
|
+const handleFileDownload = (row: any) => {
|
|
|
+ // 如果from_system的值是TOPOCEAN_KSMART,不需要拼接url
|
|
|
+ const url = row?.url
|
|
|
+ // 创建一个隐藏的 <a> 标签
|
|
|
+ const link = document.createElement('a')
|
|
|
+ link.href = row?.is_topocean ? url : import.meta.env.VITE_API_HOST + '/' + url
|
|
|
+ link.target = '_blank'
|
|
|
+
|
|
|
+ // 指定下载文件名(可选)
|
|
|
+ // link.download = row?.file_name || 'file'
|
|
|
+
|
|
|
+ // 添加到 DOM 中,触发点击事件,然后移除
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+}
|
|
|
+
|
|
|
+const getFileNameFromContentDisposition = (contentDisposition) => {
|
|
|
+ const filenameStart = contentDisposition.indexOf('filename=')
|
|
|
+ if (filenameStart === -1) return null // 如果没有找到,直接返回
|
|
|
+
|
|
|
+ const substring = contentDisposition.slice(filenameStart + 9) // 9 是 'filename='.length
|
|
|
+
|
|
|
+ const firstQuote = substring.indexOf('"')
|
|
|
+
|
|
|
+ if (firstQuote === -1) return null // 如果没有找到开始引号,直接返回
|
|
|
+
|
|
|
+ const secondQuote = substring.indexOf('"', firstQuote + 1)
|
|
|
+
|
|
|
+ if (secondQuote === -1) return null // 如果没有找到结束引号,直接返回
|
|
|
+
|
|
|
+ return substring.slice(firstQuote + 1, secondQuote)
|
|
|
+}
|
|
|
+const handleDownloadAllSelectedFiles = (label?: string) => {
|
|
|
+ const selectedFiles = []
|
|
|
+ attachmentData.value.forEach((item) => {
|
|
|
+ item?.typeList?.forEach((type) => {
|
|
|
+ // 如果选择了 label,则只下载该类型的附件
|
|
|
+ if (label && type.label !== label) return
|
|
|
+ type?.attachmentList?.forEach((attach) => {
|
|
|
+ if (attach.isSelect) {
|
|
|
+ selectedFiles.push(attach)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+ if (selectedFiles.length === 0) {
|
|
|
+ ElMessage.warning('Please select at least one file to download.')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ $api
|
|
|
+ .downloadAttachment({
|
|
|
+ data: selectedFiles
|
|
|
+ })
|
|
|
+ .then((res: any) => {
|
|
|
+ if (res.status !== 200) {
|
|
|
+ ElMessageBox.alert('The request failed. Please try again later', 'Prompt', {
|
|
|
+ confirmButtonText: 'OK',
|
|
|
+ confirmButtonClass: 'el-button--dark'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (res.data?.code === 403) {
|
|
|
+ sessionStorage.clear()
|
|
|
+ emitter.emit('login-out')
|
|
|
+ router.push('/login')
|
|
|
+ userStore.logout()
|
|
|
+ ElMessage.warning({
|
|
|
+ message: 'Please log in to use this feature.',
|
|
|
+ grouping: true
|
|
|
+ })
|
|
|
+ return
|
|
|
+ } else if (res.data?.code === 500) {
|
|
|
+ ElMessageBox.alert(res.data.message, 'Prompt', {
|
|
|
+ confirmButtonText: 'OK',
|
|
|
+ confirmButtonClass: 'el-button--dark'
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const fileName = getFileNameFromContentDisposition(res.headers['content-disposition'])
|
|
|
+ const blob = new Blob([res.data], { type: 'application/zip' })
|
|
|
+ const downloadUrl = window.URL.createObjectURL(blob)
|
|
|
+ const a = document.createElement('a')
|
|
|
+ a.download = fileName
|
|
|
+ a.href = downloadUrl
|
|
|
+ document.body.appendChild(a)
|
|
|
+ a.click()
|
|
|
+ window.URL.revokeObjectURL(downloadUrl)
|
|
|
+ document.body.removeChild(a)
|
|
|
+ })
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="tracking-download-attachment">
|
|
|
+ <div class="left-select-section">
|
|
|
+ <div class="header-select-all">
|
|
|
+ <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
|
|
|
+ :disabled="!attItem?.typeList?.length"
|
|
|
+ @change="handleParentToggle(attItem)"
|
|
|
+ v-model="attItem.isSelect"
|
|
|
+ >
|
|
|
+ <span class="font_family icon-icon_ocean_b"></span>
|
|
|
+ <el-tooltip effect="dark" :content="`Attachment ${attItem.no}`" placement="top">
|
|
|
+ <span class="label ellipsis-text">Attachment {{ attItem.no }}</span>
|
|
|
+ </el-tooltip>
|
|
|
+ </el-checkbox>
|
|
|
+ </div>
|
|
|
+ <div class="attachment-content">
|
|
|
+ <div
|
|
|
+ class="attachment-type"
|
|
|
+ v-for="typeItem in attItem?.typeList"
|
|
|
+ :key="typeItem.label"
|
|
|
+ >
|
|
|
+ <div class="type-label">
|
|
|
+ {{ typeItem.label }} ({{ typeItem.attachmentList.length }})
|
|
|
+ </div>
|
|
|
+ <div class="type-attachment-list">
|
|
|
+ <div
|
|
|
+ class="attachment-file"
|
|
|
+ v-for="fileItem in typeItem.attachmentList"
|
|
|
+ :key="fileItem.name"
|
|
|
+ >
|
|
|
+ <el-checkbox v-model="fileItem.isSelect" @change="handleChildToggle(attItem)">
|
|
|
+ <span>{{ fileItem.name }}</span></el-checkbox
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ @click="handleFileDownload(fileItem)"
|
|
|
+ class="font_family icon-icon_download_b"
|
|
|
+ ></span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="empty-attachment" v-if="!attItem?.typeList?.length">no file</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="right-summary-section">
|
|
|
+ <div class="title">Attachment Summary</div>
|
|
|
+ <div class="summary-content">
|
|
|
+ <el-button
|
|
|
+ class="el-button--main el-button--pain-theme"
|
|
|
+ style="width: 100%; margin-bottom: 8px"
|
|
|
+ @click="handleDownloadAllSelectedFiles()"
|
|
|
+ >
|
|
|
+ <span class="font_family icon-icon_download_b"></span>
|
|
|
+ <span>Download Selected ({{ allChooseFiles }})</span>
|
|
|
+ </el-button>
|
|
|
+ <el-collapse
|
|
|
+ style="margin: 8px 0"
|
|
|
+ expand-icon-position="left"
|
|
|
+ v-for="(typeItem, index) in summaryList"
|
|
|
+ :key="index"
|
|
|
+ >
|
|
|
+ <div class="right-download">
|
|
|
+ <div class="count" v-if="typeItem?.attachmentList?.length">
|
|
|
+ <span>{{ typeItem?.attachmentList?.length }}</span>
|
|
|
+ </div>
|
|
|
+ <span
|
|
|
+ @click="handleDownloadAllSelectedFiles(typeItem.label)"
|
|
|
+ class="font_family icon-icon_download_b"
|
|
|
+ ></span>
|
|
|
+ </div>
|
|
|
+ <el-collapse-item :title="typeItem.label" :name="index.toString()">
|
|
|
+ <template #icon="{ isActive }">
|
|
|
+ <span
|
|
|
+ :class="{ 'is-active': isActive }"
|
|
|
+ class="font_family icon-icon_up_b custom-arrow"
|
|
|
+ ></span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <div class="attachment-list">
|
|
|
+ <div
|
|
|
+ class="attachment-item"
|
|
|
+ v-for="attItem in typeItem?.attachmentList"
|
|
|
+ :key="attItem.name"
|
|
|
+ >
|
|
|
+ <v-ellipsis-tooltip
|
|
|
+ :max-width="246"
|
|
|
+ :max-height="32"
|
|
|
+ :line-clamp="1"
|
|
|
+ :content="attItem.name"
|
|
|
+ ></v-ellipsis-tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
+ <div class="empty-file-data" v-if="!summaryList?.length">
|
|
|
+ <img src="./images/empty-img.png" alt="empty-data" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.tracking-download-attachment {
|
|
|
+ display: flex;
|
|
|
+ height: 100%;
|
|
|
+ padding-left: 24px;
|
|
|
+ .left-select-section {
|
|
|
+ flex: 1;
|
|
|
+ .header-select-all {
|
|
|
+ :deep(.el-checkbox__inner) {
|
|
|
+ &::after {
|
|
|
+ top: 1px;
|
|
|
+ left: 7px;
|
|
|
+ height: 14px;
|
|
|
+ width: 6px;
|
|
|
+ border-width: 2.5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ :deep(.el-checkbox__inner) {
|
|
|
+ &::after {
|
|
|
+ top: 1px;
|
|
|
+ left: 4.5px;
|
|
|
+ height: 9px;
|
|
|
+ width: 4px;
|
|
|
+ border-width: 2px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .right-summary-section {
|
|
|
+ width: 340px;
|
|
|
+ height: 100%;
|
|
|
+ border: 1px solid var(--color-border);
|
|
|
+ margin-left: 24px;
|
|
|
+ min-height: 400px;
|
|
|
+ background-color: var(--color-attchment-summary-bg);
|
|
|
+ .empty-file-data {
|
|
|
+ height: calc(100% - 4px);
|
|
|
+ padding-top: 68px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.left-select-section {
|
|
|
+ height: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ .header-select-all {
|
|
|
+ margin: 16px 0;
|
|
|
+ span {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ :deep(.el-checkbox__inner) {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ & > .attachment-list {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fill, minmax(326px, 1fr));
|
|
|
+ grid-template-rows: 320px;
|
|
|
+ gap: 8px;
|
|
|
+ padding-bottom: 36px;
|
|
|
+ height: calc(100% - 64px);
|
|
|
+ overflow: auto;
|
|
|
+ :deep(.el-checkbox__label) {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ & > .label {
|
|
|
+ margin-top: 3px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.left-select-section .attachment-list .attachment-item {
|
|
|
+ height: 320px;
|
|
|
+ border: 1px solid var(--color-border);
|
|
|
+ border-radius: 12px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .top-number {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 48px;
|
|
|
+ padding: 13px 8px;
|
|
|
+ background-color: var(--color-dialog-header-bg);
|
|
|
+ :deep(.el-checkbox) {
|
|
|
+ width: 100%;
|
|
|
+ .el-checkbox__label {
|
|
|
+ width: calc(100% - 8px);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .font_family {
|
|
|
+ font-size: 24px;
|
|
|
+ margin-right: 8px;
|
|
|
+ }
|
|
|
+ .label {
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+ .ellipsis-text {
|
|
|
+ width: calc(100% - 50px);
|
|
|
+ display: block;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ :deep(.el-checkbox__inner) {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .attachment-content {
|
|
|
+ padding: 13px 8px;
|
|
|
+ overflow: auto;
|
|
|
+ height: calc(100% - 48px);
|
|
|
+ .empty-attachment {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 100%;
|
|
|
+ color: var(--color-neutral-2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .attachment-type {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ .type-label {
|
|
|
+ margin: 5px 0;
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--color-neutral-2);
|
|
|
+ }
|
|
|
+ .type-attachment-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ border-radius: 6px;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .attachment-file {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ height: 40px;
|
|
|
+ padding: 0 8px;
|
|
|
+ background-color: var(--color-personal-preference-bg);
|
|
|
+ &:nth-child(n + 2) {
|
|
|
+ border-top: 1px solid var(--color-border);
|
|
|
+ }
|
|
|
+ :deep(.el-checkbox__inner) {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ }
|
|
|
+ .icon-icon_file_pdf {
|
|
|
+ color: #e74c3c;
|
|
|
+ margin-right: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.right-summary-section {
|
|
|
+ .title {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: 700;
|
|
|
+ padding: 16px 8px;
|
|
|
+ border-bottom: 1px solid var(--color-border);
|
|
|
+ }
|
|
|
+ .summary-content {
|
|
|
+ height: calc(100% - 84px);
|
|
|
+ padding: 16px 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-collapse {
|
|
|
+ position: relative;
|
|
|
+ padding: 0 8px;
|
|
|
+ background-color: var(--color-mode);
|
|
|
+ border: 1px solid var(--color-border);
|
|
|
+ border-radius: 12px;
|
|
|
+ overflow: hidden;
|
|
|
+ :deep(.el-collapse-item__wrap) {
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+ :deep(.el-collapse-item__header) {
|
|
|
+ gap: 3px;
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+ :deep(.el-collapse-item__title) {
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ .right-download {
|
|
|
+ position: absolute;
|
|
|
+ right: 14px;
|
|
|
+ top: 14px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16px;
|
|
|
+ }
|
|
|
+ .count {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 16px;
|
|
|
+ // padding-top: 1px;
|
|
|
+ padding-left: 5px;
|
|
|
+ padding-right: 4px;
|
|
|
+ min-width: 16px;
|
|
|
+ background-color: var(--color-theme);
|
|
|
+ border-radius: 9px;
|
|
|
+ font-size: 10px;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 18px;
|
|
|
+ text-align: center;
|
|
|
+ span {
|
|
|
+ color: var(--color-white);
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .custom-arrow {
|
|
|
+ transform: rotate(90deg);
|
|
|
+ transition: transform 0.3s ease;
|
|
|
+ transform: rotate(90deg);
|
|
|
+ }
|
|
|
+
|
|
|
+ .custom-arrow.is-active {
|
|
|
+ transform: rotate(180deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .attachment-list {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ .attachment-item {
|
|
|
+ height: 32px;
|
|
|
+ padding: 7px 8px 0;
|
|
|
+ border-bottom: 1px solid var(--color-border);
|
|
|
+ background-color: var(--color-personal-preference-bg);
|
|
|
+ &:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+ span {
|
|
|
+ color: var(--color-neutral-2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|