This commit is contained in:
Lan
2024-09-26 00:23:25 +08:00
parent 3fafe768e2
commit fa12db51b2
4 changed files with 410 additions and 180 deletions
+1
View File
@@ -16,6 +16,7 @@
"lru-cache": "^11.0.1",
"lucide-vue-next": "^0.445.0",
"pinia": "^2.2.2",
"qrcode.vue": "^3.4.1",
"vue": "^3.5.8",
"vue-router": "^4.4.5"
},
+10
View File
@@ -21,6 +21,7 @@ specifiers:
pinia: ^2.2.2
postcss: ^8.4.47
prettier: ^3.3.3
qrcode.vue: ^3.4.1
rimraf: ^6.0.1
tailwindcss: ^3.4.13
typescript: ~5.4.5
@@ -34,6 +35,7 @@ dependencies:
lru-cache: 11.0.1
lucide-vue-next: 0.445.0_vue@3.5.8
pinia: 2.2.2_typescript@5.4.5+vue@3.5.8
qrcode.vue: 3.4.1_vue@3.5.8
vue: 3.5.8_typescript@5.4.5
vue-router: 4.4.5_vue@3.5.8
@@ -2734,6 +2736,14 @@ packages:
engines: {node: '>=6'}
dev: true
/qrcode.vue/3.4.1_vue@3.5.8:
resolution: {integrity: sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==}
peerDependencies:
vue: ^3.0.0
dependencies:
vue: 3.5.8_typescript@5.4.5
dev: false
/queue-microtask/1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
+58 -20
View File
@@ -79,7 +79,7 @@
<!-- 抽屉式取件记录 -->
<transition name="drawer">
<div v-if="showDrawer" class="fixed inset-y-0 right-0 w-full sm:w-96 bg-opacity-95 backdrop-filter backdrop-blur-lg shadow-2xl z-50 overflow-hidden flex flex-col" :class="[isDarkMode ? 'bg-gray-900' : 'bg-white']">
<div v-if="showDrawer" class="fixed inset-y-0 right-0 w-full sm:w-96 bg-opacity-70 backdrop-filter backdrop-blur-xl shadow-2xl z-50 overflow-hidden flex flex-col" :class="[isDarkMode ? 'bg-gray-900' : 'bg-white']">
<div class="flex justify-between items-center p-6 border-b" :class="[isDarkMode ? 'border-gray-700' : 'border-gray-200']">
<h3 class="text-2xl font-bold" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">取件记录</h3>
<button @click="toggleDrawer" class="hover:text-white transition duration-300" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-800']">
@@ -88,16 +88,21 @@
</div>
<div class="flex-grow overflow-y-auto p-6">
<transition-group name="list" tag="div" class="space-y-4">
<div v-for="record in records" :key="record.id" class="bg-opacity-50 rounded-lg p-4 flex justify-between items-center" :class="[isDarkMode ? 'bg-gray-800' : 'bg-gray-100']">
<div v-for="record in records" :key="record.id" class="bg-opacity-50 rounded-lg p-4 flex justify-between items-center shadow-md hover:shadow-lg transition duration-300 transform hover:scale-102" :class="[isDarkMode ? 'bg-gray-800 hover:bg-gray-700' : 'bg-gray-100 hover:bg-white']">
<div class="flex items-center space-x-4">
<div class="flex-shrink-0">
<FileIcon class="w-10 h-10" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
</div>
<div>
<p class="font-medium" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">{{ record.filename }}</p>
<p class="text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']">{{ record.date }}</p>
<p class="font-medium text-lg" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">{{ record.filename }}</p>
<p class="text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']">{{ record.date }} · {{ record.size }}</p>
</div>
</div>
<div class="flex space-x-2">
<button @click="viewDetails(record)" class="hover:text-indigo-300 transition duration-300" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']">
<button @click="viewDetails(record)" class="p-2 rounded-full hover:bg-opacity-20 transition duration-300" :class="[isDarkMode ? 'hover:bg-indigo-400 text-indigo-400' : 'hover:bg-indigo-100 text-indigo-600']">
<EyeIcon class="w-5 h-5" />
</button>
<button @click="deleteRecord(record.id)" class="hover:text-red-300 transition duration-300" :class="[isDarkMode ? 'text-red-400' : 'text-red-600']">
<button @click="deleteRecord(record.id)" class="p-2 rounded-full hover:bg-opacity-20 transition duration-300" :class="[isDarkMode ? 'hover:bg-red-400 text-red-400' : 'hover:bg-red-100 text-red-600']">
<TrashIcon class="w-5 h-5" />
</button>
</div>
@@ -110,13 +115,38 @@
<!-- 记录详情弹窗 -->
<transition name="fade">
<div v-if="selectedRecord" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="p-6 rounded-lg max-w-md w-full mx-4" :class="[isDarkMode ? 'bg-gray-800' : 'bg-white']">
<h3 class="text-xl font-bold mb-4" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">文件详情</h3>
<p class="mb-2" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">文件名</span>{{ selectedRecord.filename }}</p>
<p class="mb-2" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">取件日期</span>{{ selectedRecord.date }}</p>
<p class="mb-2" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">文件大小</span>{{ selectedRecord.size }}</p>
<p class="mb-4" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">下载次数</span>{{ selectedRecord.downloads }}</p>
<button @click="selectedRecord = null" class="bg-indigo-500 text-white px-4 py-2 rounded hover:bg-indigo-600 transition duration-300">关闭</button>
<div class="p-8 rounded-2xl max-w-md w-full mx-4 shadow-2xl transform transition-all duration-300 ease-out backdrop-filter backdrop-blur-lg bg-opacity-70" :class="[isDarkMode ? 'bg-gray-800' : 'bg-white']"> <h3 class="text-2xl font-bold mb-6" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">文件详情</h3>
<div class="space-y-4">
<div class="flex items-center">
<FileIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">文件</span>{{ selectedRecord.filename }}</p>
</div>
<div class="flex items-center">
<CalendarIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">取件日期</span>{{ selectedRecord.date }}</p>
</div>
<div class="flex items-center">
<HardDriveIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">文件大小</span>{{ selectedRecord.size }}</p>
</div>
<div class="flex items-center">
<DownloadIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">下载次数</span>{{ selectedRecord.downloads }}</p>
</div>
</div>
<!-- 取件二维码部分 -->
<div class="mt-6 flex flex-col items-center">
<h4 class="text-lg font-semibold mb-3" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">取件二维码</h4>
<div class="bg-white p-2 rounded-lg shadow-md">
<QRCode :value="getQRCodeValue(selectedRecord)" :size="128" level="M" />
</div>
<p class="mt-2 text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']">扫描二维码快速取件</p>
</div>
<button @click="selectedRecord = null" class="mt-8 w-full bg-gradient-to-r from-indigo-500 to-purple-600 text-white px-6 py-3 rounded-lg font-medium hover:from-indigo-600 hover:to-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-opacity-50 transition duration-300 transform hover:scale-105">
关闭
</button>
</div>
</div>
</transition>
@@ -125,8 +155,9 @@
<script setup>
import { ref, inject } from 'vue'
import { BoxIcon, EyeIcon, EyeOffIcon, ArrowRightIcon, ShieldCheckIcon, ClipboardListIcon, XIcon, TrashIcon } from 'lucide-vue-next'
import { BoxIcon, EyeIcon, EyeOffIcon, ArrowRightIcon, ShieldCheckIcon, ClipboardListIcon, XIcon, TrashIcon, FileIcon, CalendarIcon, HardDriveIcon, DownloadIcon } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import QRCode from 'qrcode.vue' // 导入 QRCode 组件
const router = useRouter()
const isDarkMode = inject('isDarkMode')
@@ -139,11 +170,11 @@ const selectedRecord = ref(null)
const showDrawer = ref(false)
const records = ref([
{ id: 1, filename: '重要文档.pdf', date: '2023-05-15', size: '2.5 MB', downloads: 3 },
{ id: 2, filename: '会议记录.docx', date: '2023-05-10', size: '1.2 MB', downloads: 2 },
{ id: 3, filename: '财务报表.xlsx', date: '2023-05-05', size: '3.7 MB', downloads: 5 },
{ id: 4, filename: '项目计划.pptx', date: '2023-05-01', size: '5.1 MB', downloads: 1 },
{ id: 5, filename: '客户名单.csv', date: '2023-04-28', size: '0.8 MB', downloads: 4 },
{ id: 1, filename: '重要文档.pdf', date: '2023-05-15', size: '2.5 MB', downloads: 3, qrCode: 'path/to/qr-code-1.png' },
{ id: 2, filename: '会议记录.docx', date: '2023-05-10', size: '1.2 MB', downloads: 2, qrCode: 'path/to/qr-code-2.png' },
{ id: 3, filename: '财务报表.xlsx', date: '2023-05-05', size: '3.7 MB', downloads: 5, qrCode: 'path/to/qr-code-3.png' },
{ id: 4, filename: '项目计划.pptx', date: '2023-05-01', size: '5.1 MB', downloads: 1, qrCode: 'path/to/qr-code-4.png' },
{ id: 5, filename: '客户名单.csv', date: '2023-04-28', size: '0.8 MB', downloads: 4, qrCode: 'path/to/qr-code-5.png' },
])
const togglePasswordVisibility = () => {
@@ -164,7 +195,8 @@ const handleSubmit = () => {
filename: `新文件${records.value.length + 1}.pdf`,
date: new Date().toISOString().split('T')[0],
size: `${Math.random().toFixed(1)} MB`,
downloads: 0
downloads: 0,
qrCode: `path/to/qr-code-${records.value.length + 1}.png`
}
records.value.unshift(newRecord)
showDrawer.value = true
@@ -190,6 +222,12 @@ const toggleDrawer = () => {
const toSend = () => {
router.push('/send')
}
const getQRCodeValue = (record) => {
// 这里返回你想要在二维码中编码的信息
// 例如,可以是一个包含文件ID的URL
return `https://your-domain.com/retrieve/${record.id}`
}
</script>
<style scoped>
+184 -3
View File
@@ -1,13 +1,14 @@
<template>
<div class="min-h-screen flex items-center justify-center p-4 overflow-hidden transition-colors duration-300">
<div
class="rounded-3xl shadow-2xl overflow-hidden border p-8 w-full max-w-md transition-colors duration-300"
class="rounded-3xl shadow-2xl overflow-hidden border w-full max-w-md transition-colors duration-300"
:class="[
isDarkMode
? 'bg-white bg-opacity-10 backdrop-filter backdrop-blur-xl border-gray-700'
: 'bg-white border-gray-200'
]"
>
<div class="p-8">
<h2
class="text-3xl font-extrabold text-center mb-8 cursor-pointer transition-colors duration-300"
:class="[
@@ -169,14 +170,114 @@
</router-link>
</div>
</div>
<div class="px-8 py-4 bg-opacity-50 flex justify-between items-center" :class="[isDarkMode ? 'bg-gray-800' : 'bg-gray-100']">
<span class="text-sm flex items-center" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']">
<ShieldCheckIcon class="w-4 h-4 mr-1 text-green-400" />
安全加密
</span>
<button @click="toggleDrawer" class="text-sm hover:text-indigo-300 transition duration-300 flex items-center" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']">
发件记录
<ClipboardListIcon class="w-4 h-4 ml-1" />
</button>
</div>
</div>
<!-- 抽屉式发件记录 -->
<transition name="drawer">
<div v-if="showDrawer" class="fixed inset-y-0 right-0 w-full sm:w-96 bg-opacity-70 backdrop-filter backdrop-blur-xl shadow-2xl z-50 overflow-hidden flex flex-col" :class="[isDarkMode ? 'bg-gray-900' : 'bg-white']">
<div class="flex justify-between items-center p-6 border-b" :class="[isDarkMode ? 'border-gray-700' : 'border-gray-200']">
<h3 class="text-2xl font-bold" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">发件记录</h3>
<button @click="toggleDrawer" class="hover:text-white transition duration-300" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-800']">
<XIcon class="w-6 h-6" />
</button>
</div>
<div class="flex-grow overflow-y-auto p-6">
<transition-group name="list" tag="div" class="space-y-4">
<div v-for="record in sendRecords" :key="record.id" class="bg-opacity-50 rounded-lg p-4 flex justify-between items-center shadow-md hover:shadow-lg transition duration-300 transform hover:scale-102" :class="[isDarkMode ? 'bg-gray-800 hover:bg-gray-700' : 'bg-gray-100 hover:bg-white']">
<div class="flex items-center space-x-4">
<div class="flex-shrink-0">
<FileIcon class="w-10 h-10" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
</div>
<div>
<p class="font-medium text-lg" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">{{ record.filename }}</p>
<p class="text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']">{{ record.date }} · {{ record.size }}</p>
</div>
</div>
<div class="flex space-x-2">
<button @click="viewDetails(record)" class="p-2 rounded-full hover:bg-opacity-20 transition duration-300" :class="[isDarkMode ? 'hover:bg-indigo-400 text-indigo-400' : 'hover:bg-indigo-100 text-indigo-600']">
<EyeIcon class="w-5 h-5" />
</button>
<button @click="deleteRecord(record.id)" class="p-2 rounded-full hover:bg-opacity-20 transition duration-300" :class="[isDarkMode ? 'hover:bg-red-400 text-red-400' : 'hover:bg-red-100 text-red-600']">
<TrashIcon class="w-5 h-5" />
</button>
</div>
</div>
</transition-group>
</div>
</div>
</transition>
<!-- 记录详情弹窗 -->
<transition name="fade">
<div v-if="selectedRecord" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="p-8 rounded-2xl max-w-md w-full mx-4 shadow-2xl transform transition-all duration-300 ease-out backdrop-filter backdrop-blur-lg bg-opacity-70" :class="[isDarkMode ? 'bg-gray-800' : 'bg-white']">
<h3 class="text-2xl font-bold mb-6" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">文件详情</h3>
<div class="space-y-4">
<div class="flex items-center">
<FileIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">文件名</span>{{ selectedRecord.filename }}</p>
</div>
<div class="flex items-center">
<CalendarIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">发送日期</span>{{ selectedRecord.date }}</p>
</div>
<div class="flex items-center">
<HardDriveIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">文件大小</span>{{ selectedRecord.size }}</p>
</div>
<div class="flex items-center">
<ClockIcon class="w-6 h-6 mr-3" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-800']"><span class="font-medium">过期时间</span>{{ selectedRecord.expiration }}</p>
</div>
</div>
<!-- 取件码和二维码部分 -->
<div class="mt-6 flex justify-between items-center">
<div class="flex flex-col items-center w-1/2 pr-2">
<h4 class="text-lg font-semibold mb-3" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">取件码</h4>
<div
class="bg-gray-100 p-3 rounded-lg shadow-md cursor-pointer hover:bg-gray-200 transition-colors duration-300 w-full text-center"
@click="copyRetrieveCode(selectedRecord.retrieveCode)"
>
<p class="text-2xl font-bold text-indigo-600">{{ selectedRecord.retrieveCode }}</p>
</div>
<p class="mt-2 text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']">点击复制取件码</p>
</div>
<div class="flex flex-col items-center w-1/2 pl-2">
<h4 class="text-lg font-semibold mb-3" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">二维码</h4>
<div class="bg-white p-2 rounded-lg shadow-md">
<QRCode :value="getQRCodeValue(selectedRecord)" :size="128" level="M" />
</div>
<p class="mt-2 text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']">扫描二维码快速取件</p>
</div>
</div>
<button @click="selectedRecord = null" class="mt-8 w-full bg-gradient-to-r from-indigo-500 to-purple-600 text-white px-6 py-3 rounded-lg font-medium hover:from-indigo-600 hover:to-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-opacity-50 transition duration-300 transform hover:scale-105">
关闭
</button>
</div>
</div>
</transition>
</div>
</template>
<script setup lang="ts">
import { ref, inject, onMounted } from 'vue'
import { UploadCloudIcon, SendIcon } from 'lucide-vue-next'
import { UploadCloudIcon, SendIcon, ClipboardListIcon, XIcon, TrashIcon, FileIcon, CalendarIcon, HardDriveIcon, ClockIcon, EyeIcon, ShieldCheckIcon } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import BorderProgressBar from '../components/BorderProgressBar.vue'
import QRCode from 'qrcode.vue'
const router = useRouter()
const isDarkMode = inject('isDarkMode')
@@ -189,6 +290,16 @@ const expirationMethod = ref('time')
const expirationTime = ref('')
const expirationViews = ref('')
const uploadProgress = ref(0)
const showDrawer = ref(false)
const selectedRecord = ref<any>(null)
const sendRecords = ref([
{ id: 1, filename: '项目报告.pdf', date: '2023-05-15', size: '3.2 MB', expiration: '2023-05-22', retrieveCode: 'ABC123' },
{ id: 2, filename: '会议纪要.docx', date: '2023-05-10', size: '1.5 MB', expiration: '2023-05-17', retrieveCode: 'DEF456' },
{ id: 3, filename: '财务数据.xlsx', date: '2023-05-05', size: '2.8 MB', expiration: '2023-05-12', retrieveCode: 'GHI789' },
{ id: 4, filename: '产品设计.psd', date: '2023-05-01', size: '15.7 MB', expiration: '2023-05-08', retrieveCode: 'JKL012' },
{ id: 5, filename: '客户反馈.txt', date: '2023-04-28', size: '0.5 MB', expiration: '2023-05-05', retrieveCode: 'MNO345' },
])
const triggerFileUpload = () => {
fileInput.value?.click()
@@ -244,19 +355,70 @@ const handleSubmit = () => {
return
}
// 这里处理文件/文本上传和过期设置的逻辑
// 处理文件/文本上传和过期设置的逻辑
console.log('Send Type:', sendType.value)
console.log('Selected File:', selectedFile.value)
console.log('Text Content:', textContent.value)
console.log('Expiration Method:', expirationMethod.value)
console.log('Expiration Time:', expirationTime.value)
console.log('Expiration Views:', expirationViews.value)
// 添加新的发送记录
const newRecord = {
id: sendRecords.value.length + 1,
filename: selectedFile.value ? selectedFile.value.name : '文本内容.txt',
date: new Date().toISOString().split('T')[0],
size: selectedFile.value ? `${(selectedFile.value.size / (1024 * 1024)).toFixed(1)} MB` : '0.1 MB',
expiration: expirationMethod.value === 'time'
? new Date(Date.now() + parseInt(expirationTime.value) * 60 * 60 * 1000).toISOString().split('T')[0]
: `${expirationViews.value}次查看后过期`,
retrieveCode: Math.random().toString(36).substring(2, 8).toUpperCase()
}
sendRecords.value.unshift(newRecord)
// 显示发送成功消息
alert('文件发送成功!取件码:' + newRecord.retrieveCode)
// 重置表单
selectedFile.value = null
textContent.value = ''
expirationTime.value = ''
expirationViews.value = ''
uploadProgress.value = 0
}
const toRetrieve = () => {
router.push('/')
}
const toggleDrawer = () => {
showDrawer.value = !showDrawer.value
}
const viewDetails = (record:any) => {
selectedRecord.value = record
}
const deleteRecord = (id:any) => {
sendRecords.value = sendRecords.value.filter(record => record.id !== id)
}
const getQRCodeValue = (record:any) => {
// 这里返回你想要在二维码中编码的信息
// 例如,可以是一个包含文件ID和取件码的URL
return `https://your-domain.com/retrieve/${record.id}?code=${record.retrieveCode}`
}
const copyRetrieveCode = async (code:any) => {
try {
await navigator.clipboard.writeText(code)
alert('取件码已复制到剪贴板')
} catch (err) {
console.error('无法复制取件码: ', err)
alert('复制失败,请手动复制取件码')
}
}
// 使用 onMounted 钩子延迟加载一些非关键资源或初始化
onMounted(() => {
// 这里可以放置一些非立即需要的初始化代码
@@ -281,4 +443,23 @@ onMounted(() => {
opacity: 1;
transform: translateY(0);
}
.drawer-enter-active,
.drawer-leave-active {
transition: transform 0.3s ease;
}
.drawer-enter-from,
.drawer-leave-to {
transform: translateX(100%);
}
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>