This commit is contained in:
+85
-25
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen flex items-center justify-center p-4 overflow-hidden transition-colors duration-300">
|
<div class="min-h-screen flex items-center justify-center p-4 overflow-hidden transition-colors duration-300"
|
||||||
|
@paste.prevent="handlePaste">
|
||||||
<div class="rounded-3xl shadow-2xl overflow-hidden border w-full max-w-md transition-colors duration-300" :class="[
|
<div class="rounded-3xl shadow-2xl overflow-hidden border w-full max-w-md transition-colors duration-300" :class="[
|
||||||
isDarkMode
|
isDarkMode
|
||||||
? 'bg-white bg-opacity-10 backdrop-filter backdrop-blur-xl border-gray-700'
|
? 'bg-white bg-opacity-10 backdrop-filter backdrop-blur-xl border-gray-700'
|
||||||
@@ -41,7 +42,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div v-if="sendType === 'file'" key="file" class="grid grid-cols-1 gap-8" @paste.prevent="handlePaste">
|
<div v-if="sendType === 'file'" key="file" class="grid grid-cols-1 gap-8">
|
||||||
<!-- 文件上传区域 -->
|
<!-- 文件上传区域 -->
|
||||||
<div
|
<div
|
||||||
class="rounded-xl p-8 flex flex-col items-center justify-center border-2 border-dashed transition-all duration-300 group cursor-pointer relative"
|
class="rounded-xl p-8 flex flex-col items-center justify-center border-2 border-dashed transition-all duration-300 group cursor-pointer relative"
|
||||||
@@ -214,13 +215,13 @@
|
|||||||
|
|
||||||
<!-- 记录详情弹窗 -->
|
<!-- 记录详情弹窗 -->
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div v-if="selectedRecord" class="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-3 sm:p-4 overflow-y-auto">
|
<div v-if="selectedRecord"
|
||||||
|
class="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50 p-3 sm:p-4 overflow-y-auto">
|
||||||
<div
|
<div
|
||||||
class="w-full max-w-2xl rounded-2xl shadow-2xl transform transition-all duration-300 ease-out overflow-hidden"
|
class="w-full max-w-2xl rounded-2xl shadow-2xl transform transition-all duration-300 ease-out overflow-hidden"
|
||||||
:class="[isDarkMode ? 'bg-gray-900 bg-opacity-70' : 'bg-white bg-opacity-95']">
|
:class="[isDarkMode ? 'bg-gray-900 bg-opacity-70' : 'bg-white bg-opacity-95']">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div class="px-4 sm:px-6 py-3 sm:py-4 border-b"
|
<div class="px-4 sm:px-6 py-3 sm:py-4 border-b" :class="[isDarkMode ? 'border-gray-800' : 'border-gray-100']">
|
||||||
:class="[isDarkMode ? 'border-gray-800' : 'border-gray-100']">
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h3 class="text-lg sm:text-xl font-semibold" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
<h3 class="text-lg sm:text-xl font-semibold" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
||||||
文件详情
|
文件详情
|
||||||
@@ -239,7 +240,8 @@
|
|||||||
:class="[isDarkMode ? 'bg-gray-800 bg-opacity-50' : 'bg-gray-50 bg-opacity-95']">
|
:class="[isDarkMode ? 'bg-gray-800 bg-opacity-50' : 'bg-gray-50 bg-opacity-95']">
|
||||||
<div class="flex items-center mb-3 sm:mb-4">
|
<div class="flex items-center mb-3 sm:mb-4">
|
||||||
<div class="p-2 sm:p-3 rounded-lg" :class="[isDarkMode ? 'bg-gray-800' : 'bg-white']">
|
<div class="p-2 sm:p-3 rounded-lg" :class="[isDarkMode ? 'bg-gray-800' : 'bg-white']">
|
||||||
<FileIcon class="w-5 h-5 sm:w-6 sm:h-6" :class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
|
<FileIcon class="w-5 h-5 sm:w-6 sm:h-6"
|
||||||
|
:class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-600']" />
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3 sm:ml-4">
|
<div class="ml-3 sm:ml-4">
|
||||||
<h4 class="font-medium text-sm sm:text-base" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
<h4 class="font-medium text-sm sm:text-base" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
||||||
@@ -252,13 +254,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 gap-3 sm:gap-4">
|
<div class="grid grid-cols-2 gap-3 sm:gap-4">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<ClockIcon class="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-1.5 sm:mr-2" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']" />
|
<ClockIcon class="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-1.5 sm:mr-2"
|
||||||
|
:class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']" />
|
||||||
<span class="text-xs sm:text-sm" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-600']">
|
<span class="text-xs sm:text-sm" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-600']">
|
||||||
{{ selectedRecord.expiration }}
|
{{ selectedRecord.expiration }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<ShieldCheckIcon class="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-1.5 sm:mr-2" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']" />
|
<ShieldCheckIcon class="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-1.5 sm:mr-2"
|
||||||
|
:class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']" />
|
||||||
<span class="text-xs sm:text-sm" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-600']">
|
<span class="text-xs sm:text-sm" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-600']">
|
||||||
安全加密
|
安全加密
|
||||||
</span>
|
</span>
|
||||||
@@ -278,22 +282,26 @@
|
|||||||
<ClipboardCopyIcon class="w-4 h-4 sm:w-5 sm:h-5" />
|
<ClipboardCopyIcon class="w-4 h-4 sm:w-5 sm:h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-2xl sm:text-3xl font-bold tracking-wider text-center">{{ selectedRecord.retrieveCode }}</p>
|
<p class="text-2xl sm:text-3xl font-bold tracking-wider text-center">{{ selectedRecord.retrieveCode }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="rounded-xl p-3 sm:p-4"
|
<div class="rounded-xl p-3 sm:p-4"
|
||||||
:class="[isDarkMode ? 'bg-gray-800 bg-opacity-50' : 'bg-gray-50 bg-opacity-95']">
|
:class="[isDarkMode ? 'bg-gray-800 bg-opacity-50' : 'bg-gray-50 bg-opacity-95']">
|
||||||
<div class="flex items-center justify-between mb-2 sm:mb-3">
|
<div class="flex items-center justify-between mb-2 sm:mb-3">
|
||||||
<h4 class="font-medium text-sm sm:text-base flex items-center" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
<h4 class="font-medium text-sm sm:text-base flex items-center"
|
||||||
|
:class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
||||||
<TerminalIcon class="w-4 h-4 sm:w-5 sm:h-5 mr-1.5 sm:mr-2 text-indigo-500" />
|
<TerminalIcon class="w-4 h-4 sm:w-5 sm:h-5 mr-1.5 sm:mr-2 text-indigo-500" />
|
||||||
wget下载
|
wget下载
|
||||||
</h4>
|
</h4>
|
||||||
<button @click="copyWgetCommand(selectedRecord.retrieveCode)"
|
<button @click="copyWgetCommand(selectedRecord.retrieveCode)"
|
||||||
class="p-1.5 sm:p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
class="p-1.5 sm:p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||||
<ClipboardCopyIcon class="w-4 h-4 sm:w-5 sm:h-5" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']" />
|
<ClipboardCopyIcon class="w-4 h-4 sm:w-5 sm:h-5"
|
||||||
|
:class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-xs sm:text-sm font-mono break-all" :class="[isDarkMode ? 'text-gray-300' : 'text-gray-600']">
|
<p class="text-xs sm:text-sm font-mono break-all"
|
||||||
|
:class="[isDarkMode ? 'text-gray-300' : 'text-gray-600']">
|
||||||
点击复制wget命令
|
点击复制wget命令
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -303,7 +311,8 @@
|
|||||||
<div class="rounded-xl p-4 sm:p-5 flex flex-col items-center"
|
<div class="rounded-xl p-4 sm:p-5 flex flex-col items-center"
|
||||||
:class="[isDarkMode ? 'bg-gray-800 bg-opacity-50' : 'bg-gray-50 bg-opacity-95']">
|
:class="[isDarkMode ? 'bg-gray-800 bg-opacity-50' : 'bg-gray-50 bg-opacity-95']">
|
||||||
<div class="bg-white p-3 sm:p-4 rounded-lg shadow-sm mb-3 sm:mb-4">
|
<div class="bg-white p-3 sm:p-4 rounded-lg shadow-sm mb-3 sm:mb-4">
|
||||||
<QRCode :value="getQRCodeValue(selectedRecord)" :size="140" level="M" class="sm:w-[160px] sm:h-[160px]" />
|
<QRCode :value="getQRCodeValue(selectedRecord)" :size="140" level="M"
|
||||||
|
class="sm:w-[160px] sm:h-[160px]" />
|
||||||
</div>
|
</div>
|
||||||
<p class="text-xs sm:text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
<p class="text-xs sm:text-sm" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
||||||
扫描二维码快速取件
|
扫描二维码快速取件
|
||||||
@@ -397,17 +406,41 @@ const handleFileDrop = async (event: DragEvent) => {
|
|||||||
const handlePaste = async (event: ClipboardEvent) => {
|
const handlePaste = async (event: ClipboardEvent) => {
|
||||||
const items = event.clipboardData?.items
|
const items = event.clipboardData?.items
|
||||||
if (!items) return
|
if (!items) return
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.kind === 'file') {
|
if (item.kind === 'file') {
|
||||||
const file = item.getAsFile()
|
const file = item.getAsFile()
|
||||||
if (file) {
|
if (file) {
|
||||||
selectedFile.value = file
|
// 检查文件是否为空
|
||||||
if (!checkUpload()) return
|
if (file.size === 0) {
|
||||||
fileHash.value = await calculateFileHash(file)
|
alertStore.showAlert('无法读取空文件', 'error')
|
||||||
alertStore.showAlert('已从剪贴板添加文件:' + file.name, 'success')
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件类型
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
selectedFile.value = file
|
||||||
|
if (!checkUpload()) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
fileHash.value = await calculateFileHash(file)
|
||||||
|
alertStore.showAlert('已从剪贴板添加图片:' + file.name, 'success')
|
||||||
|
} catch (error) {
|
||||||
|
alertStore.showAlert('文件处理失败', 'error')
|
||||||
|
console.error('File hash calculation failed:', error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alertStore.showAlert('目前仅支持粘贴图片文件', 'warning')
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
sendType.value = 'text'
|
||||||
|
items[0].getAsString((str: string) => {
|
||||||
|
textContent.value += str
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,16 +453,29 @@ const calculateFileHash = async (file: File): Promise<string> => {
|
|||||||
|
|
||||||
fileReader.onload = async (e) => {
|
fileReader.onload = async (e) => {
|
||||||
const chunk = new Uint8Array(e.target!.result as ArrayBuffer)
|
const chunk = new Uint8Array(e.target!.result as ArrayBuffer)
|
||||||
const hashBuffer = await crypto.subtle.digest('SHA-256', chunk)
|
|
||||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
|
||||||
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
|
|
||||||
|
|
||||||
currentChunk++
|
try {
|
||||||
|
// 尝试使用 crypto.subtle.digest
|
||||||
|
if (window.isSecureContext) {
|
||||||
|
const hashBuffer = await crypto.subtle.digest('SHA-256', chunk)
|
||||||
|
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||||
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
|
||||||
|
|
||||||
if (currentChunk < chunks) {
|
currentChunk++
|
||||||
loadNext()
|
if (currentChunk < chunks) {
|
||||||
} else {
|
loadNext()
|
||||||
resolve(hashHex)
|
} else {
|
||||||
|
resolve(hashHex)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果不是安全上下文(HTTP),则返回一个基于文件信息的替代哈希
|
||||||
|
const fallbackHash = generateFallbackHash(file)
|
||||||
|
resolve(fallbackHash)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 如果 crypto.subtle.digest 失败,使用替代方案
|
||||||
|
const fallbackHash = generateFallbackHash(file)
|
||||||
|
resolve(fallbackHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,6 +489,20 @@ const calculateFileHash = async (file: File): Promise<string> => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成替代哈希的函数
|
||||||
|
const generateFallbackHash = (file: File): string => {
|
||||||
|
// 使用文件名、大小和最后修改时间生成一个简单的哈希
|
||||||
|
const fileInfo = `${file.name}-${file.size}-${file.lastModified}`
|
||||||
|
let hash = 0
|
||||||
|
for (let i = 0; i < fileInfo.length; i++) {
|
||||||
|
const char = fileInfo.charCodeAt(i)
|
||||||
|
hash = ((hash << 5) - hash) + char
|
||||||
|
hash = hash & hash // Convert to 32-bit integer
|
||||||
|
}
|
||||||
|
// 转换为16进制字符串并填充到64位
|
||||||
|
return Math.abs(hash).toString(16).padStart(64, '0')
|
||||||
|
}
|
||||||
|
|
||||||
const getPlaceholder = (value: string = expirationMethod.value) => {
|
const getPlaceholder = (value: string = expirationMethod.value) => {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'day':
|
case 'day':
|
||||||
|
|||||||
Reference in New Issue
Block a user