fix:some bug
This commit is contained in:
@@ -100,14 +100,6 @@ provide('isLoading', isLoading)
|
|||||||
transition: background-color 0.5s ease;
|
transition: background-color 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.light {
|
|
||||||
@apply bg-gradient-to-br from-blue-50 via-indigo-50 to-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
@apply bg-gradient-to-br from-gray-900 via-indigo-900 to-black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { FileIcon, CalendarIcon, ClockIcon, KeyIcon, XIcon } from 'lucide-vue-next'
|
import { FileIcon, CalendarIcon, ClockIcon, KeyIcon, XIcon } from 'lucide-vue-next'
|
||||||
import QRCode from 'qrcode.vue'
|
import QRCode from 'qrcode.vue'
|
||||||
import { useAlertStore } from '@/stores/alertStore'
|
import { copyToClipboard } from '@/utils/clipboard'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
record: Object,
|
record: Object,
|
||||||
@@ -106,17 +106,10 @@ const props = defineProps({
|
|||||||
baseUrl: String
|
baseUrl: String
|
||||||
})
|
})
|
||||||
|
|
||||||
const alertStore = useAlertStore()
|
|
||||||
|
|
||||||
defineEmits(['close', 'show-preview'])
|
defineEmits(['close', 'show-preview'])
|
||||||
|
|
||||||
const copyShareLink = async () => {
|
const copyShareLink = async () => {
|
||||||
try {
|
copyToClipboard(`${props.baseUrl}/retrieve/${props.record.code}`)
|
||||||
await navigator.clipboard.writeText(`${props.baseUrl}/retrieve/${props.record.code}`)
|
|
||||||
alertStore.showAlert('链接已复制到剪贴板', 'success')
|
|
||||||
} catch (err) {
|
|
||||||
alertStore.showAlert('复制失败,请手动复制', 'error')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -236,4 +236,37 @@ input:checked + .slider {
|
|||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
transition-duration: 300ms;
|
transition-duration: 300ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-scrollbar {
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #cbd5e0;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #a0aec0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 适配暗黑模式 */
|
||||||
|
:deep(.dark &::-webkit-scrollbar-thumb) {
|
||||||
|
background-color: #4a5568;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #2d3748;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保内容区域不会被截断 */
|
||||||
|
.space-y-6 {
|
||||||
|
margin-bottom: 5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
// 从环境变量中获取 API 基础 URL
|
// 从环境变量中获取 API 基础 URL
|
||||||
const baseURL =
|
const baseURL =
|
||||||
@@ -13,7 +12,7 @@ const sanitizedBaseURL = typeof baseURL === 'string' ? baseURL : ''
|
|||||||
// 创建 axios 实例
|
// 创建 axios 实例
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: sanitizedBaseURL,
|
baseURL: sanitizedBaseURL,
|
||||||
timeout: 10000, // 请求超时时间
|
timeout: 1000000000000000, // 请求超时时间
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* 剪贴板工具函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useAlertStore } from '@/stores/alertStore'
|
||||||
|
|
||||||
|
interface CopyOptions {
|
||||||
|
successMsg?: string
|
||||||
|
errorMsg?: string
|
||||||
|
showMsg?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制文本到剪贴板
|
||||||
|
* @param text 要复制的文本
|
||||||
|
* @param options 配置选项
|
||||||
|
* @returns Promise<boolean> 是否复制成功
|
||||||
|
*/
|
||||||
|
export const copyToClipboard = async (
|
||||||
|
text: string,
|
||||||
|
options: CopyOptions = {}
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const { successMsg = '复制成功', errorMsg = '复制失败,请手动复制', showMsg = true } = options
|
||||||
|
|
||||||
|
const alertStore = useAlertStore()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 优先使用 Clipboard API
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
await navigator.clipboard.writeText(text)
|
||||||
|
if (showMsg) alertStore.showAlert(successMsg, 'success')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后备方案:使用传统的复制方法
|
||||||
|
const textarea = document.createElement('textarea')
|
||||||
|
textarea.value = text
|
||||||
|
textarea.style.position = 'fixed'
|
||||||
|
textarea.style.opacity = '0'
|
||||||
|
document.body.appendChild(textarea)
|
||||||
|
textarea.select()
|
||||||
|
const success = document.execCommand('copy')
|
||||||
|
document.body.removeChild(textarea)
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
if (showMsg) alertStore.showAlert(successMsg, 'success')
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
throw new Error('execCommand copy failed')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('复制失败:', err)
|
||||||
|
if (showMsg) alertStore.showAlert(errorMsg, 'error')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成并复制取件链接
|
||||||
|
* @param code 取件码
|
||||||
|
* @returns Promise<boolean> 是否复制成功
|
||||||
|
*/
|
||||||
|
export const copyRetrieveLink = async (code: string): Promise<boolean> => {
|
||||||
|
const link = `${window.location.origin}/#/?code=${code}`
|
||||||
|
return copyToClipboard(link, {
|
||||||
|
successMsg: '取件链接已复制到剪贴板',
|
||||||
|
errorMsg: '复制失败,请手动复制取件链接'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制取件码
|
||||||
|
* @param code 取件码
|
||||||
|
* @returns Promise<boolean> 是否复制成功
|
||||||
|
*/
|
||||||
|
export const copyRetrieveCode = async (code: string): Promise<boolean> => {
|
||||||
|
return copyToClipboard(code, {
|
||||||
|
successMsg: '取件码已复制到剪贴板',
|
||||||
|
errorMsg: '复制失败,请手动复制取件码'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// 存储单位转换
|
||||||
|
export const getStorageUnit = (value: number) => {
|
||||||
|
if (value >= 1024 * 1024 * 1024) {
|
||||||
|
return Math.round(value / (1024 * 1024 * 1024)) + 'GB'
|
||||||
|
} else if (value >= 1024 * 1024) {
|
||||||
|
return Math.round(value / (1024 * 1024)) + 'MB'
|
||||||
|
} else {
|
||||||
|
return Math.round(value / 1024) + 'KB'
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-31
@@ -92,7 +92,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p :class="['mt-2 text-xs', isDarkMode ? 'text-gray-500' : 'text-gray-400']">
|
<p :class="['mt-2 text-xs', isDarkMode ? 'text-gray-500' : 'text-gray-400']">
|
||||||
支持各种常见格式,最大20MB
|
支持各种常见格式,最大{{ getStorageUnit(config.uploadSize) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,11 +128,9 @@
|
|||||||
: 'bg-white text-gray-900 border border-gray-300'
|
: 'bg-white text-gray-900 border border-gray-300'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<option value="day">按天数</option>
|
<option v-for="item in config.expireStyle" :value="item" :key="item">
|
||||||
<option value="hour">按小时</option>
|
{{ getUnit(item) }}
|
||||||
<option value="minute">按分钟</option>
|
</option>
|
||||||
<option value="count">按查看次数</option>
|
|
||||||
<option value="forever">永久</option>
|
|
||||||
</select>
|
</select>
|
||||||
<div v-if="expirationMethod !== 'forever'" class="flex items-center space-x-2">
|
<div v-if="expirationMethod !== 'forever'" class="flex items-center space-x-2">
|
||||||
<div class="relative flex-grow">
|
<div class="relative flex-grow">
|
||||||
@@ -414,6 +412,11 @@ import QRCode from 'qrcode.vue'
|
|||||||
import SparkMD5 from 'spark-md5'
|
import SparkMD5 from 'spark-md5'
|
||||||
import { useFileDataStore } from '../stores/fileData'
|
import { useFileDataStore } from '../stores/fileData'
|
||||||
import api from '@/utils/api'
|
import api from '@/utils/api'
|
||||||
|
import { copyRetrieveLink, copyRetrieveCode } from '@/utils/clipboard'
|
||||||
|
import { getStorageUnit } from '@/utils/convert'
|
||||||
|
|
||||||
|
const config: any = JSON.parse(localStorage.getItem('config') || '{}')
|
||||||
|
console.log(config)
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const isDarkMode = inject('isDarkMode')
|
const isDarkMode = inject('isDarkMode')
|
||||||
@@ -549,8 +552,8 @@ const mergeChunks = async (fileHash: string, totalChunks: number) => {
|
|||||||
console.log(`请求合并文件切片, fileHash: ${fileHash}, totalChunks: ${totalChunks}`)
|
console.log(`请求合并文件切片, fileHash: ${fileHash}, totalChunks: ${totalChunks}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPlaceholder = () => {
|
const getPlaceholder = (value: string = expirationMethod.value) => {
|
||||||
switch (expirationMethod.value) {
|
switch (value) {
|
||||||
case 'day':
|
case 'day':
|
||||||
return '输入天数'
|
return '输入天数'
|
||||||
case 'hour':
|
case 'hour':
|
||||||
@@ -566,8 +569,8 @@ const getPlaceholder = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUnit = () => {
|
const getUnit = (value: string = expirationMethod.value) => {
|
||||||
switch (expirationMethod.value) {
|
switch (value) {
|
||||||
case 'day':
|
case 'day':
|
||||||
return '天'
|
return '天'
|
||||||
case 'hour':
|
case 'hour':
|
||||||
@@ -703,27 +706,6 @@ const getQRCodeValue = (record: any) => {
|
|||||||
return `${baseUrl}/?code=${record.retrieveCode}`
|
return `${baseUrl}/?code=${record.retrieveCode}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const copyRetrieveCode = async (code: string) => {
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(code)
|
|
||||||
alertStore.showAlert('取件码已复制到剪贴板', 'success')
|
|
||||||
} catch (err) {
|
|
||||||
console.error('无法复制取件码: ', err)
|
|
||||||
alertStore.showAlert('复制失败,请手动复制取件码', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyRetrieveLink = async (code: string) => {
|
|
||||||
const link = `${baseUrl}/?code=${code}`
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(link)
|
|
||||||
alertStore.showAlert('取件链接已复制到剪贴板', 'success')
|
|
||||||
} catch (err) {
|
|
||||||
console.error('无法复制取件链接: ', err)
|
|
||||||
alertStore.showAlert('复制失败,请动复制取件链接', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用 onMounted 钩子延迟加载一些非关键资源或初始化
|
// 使用 onMounted 钩子延迟加载一些非关键资源或初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 这里可以放置一些非立即需要的初始化代码
|
// 这里可以放置一些非立即需要的初始化代码
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="p-6 h-screen overflow-y-auto custom-scrollbar">
|
||||||
<h2 class="text-2xl font-bold mb-6" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
|
<h2 class="text-2xl font-bold mb-6" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
|
||||||
仪表盘
|
仪表盘
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="p-6 h-screen overflow-y-auto custom-scrollbar">
|
||||||
<h2 class="text-2xl font-bold mb-6" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
|
<h2 class="text-2xl font-bold mb-6" :class="[isDarkMode ? 'text-white' : 'text-gray-800']">
|
||||||
文件管理
|
文件管理
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface ConfigState {
|
|||||||
uploadCount: number
|
uploadCount: number
|
||||||
errorMinute: number
|
errorMinute: number
|
||||||
errorCount: number
|
errorCount: number
|
||||||
|
s3_proxy: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = ref<ConfigState>({
|
const config = ref<ConfigState>({
|
||||||
@@ -63,7 +64,8 @@ const config = ref<ConfigState>({
|
|||||||
s3_hostname: '',
|
s3_hostname: '',
|
||||||
uploadCount: 1,
|
uploadCount: 1,
|
||||||
errorMinute: 1,
|
errorMinute: 1,
|
||||||
errorCount: 1
|
errorCount: 1,
|
||||||
|
s3_proxy: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const fileSize = ref(1)
|
const fileSize = ref(1)
|
||||||
@@ -439,6 +441,99 @@ refreshData()
|
|||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label
|
||||||
|
class="block text-sm font-medium"
|
||||||
|
:class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']"
|
||||||
|
>
|
||||||
|
S3 Region Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="config.s3_region_name"
|
||||||
|
placeholder="auto"
|
||||||
|
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
|
||||||
|
:class="[
|
||||||
|
isDarkMode
|
||||||
|
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400 hover:border-gray-500'
|
||||||
|
: 'border-gray-300 hover:border-gray-400 placeholder-gray-500'
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label
|
||||||
|
class="block text-sm font-medium"
|
||||||
|
:class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']"
|
||||||
|
>
|
||||||
|
S3 Signature Version
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
v-model="config.s3_signature_version"
|
||||||
|
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
|
||||||
|
:class="[
|
||||||
|
isDarkMode
|
||||||
|
? 'bg-gray-700 border-gray-600 text-white hover:border-gray-500'
|
||||||
|
: 'border-gray-300 hover:border-gray-400'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<option value="s3v2">S3v2</option>
|
||||||
|
<option value="s3v4">S3v4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label
|
||||||
|
class="block text-sm font-medium"
|
||||||
|
:class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']"
|
||||||
|
>
|
||||||
|
S3 Hostname
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="config.s3_hostname"
|
||||||
|
class="w-full rounded-md shadow-sm px-4 py-2.5 transition-all duration-200 ease-in-out border focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none"
|
||||||
|
:class="[
|
||||||
|
isDarkMode
|
||||||
|
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400 hover:border-gray-500'
|
||||||
|
: 'border-gray-300 hover:border-gray-400 placeholder-gray-500'
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label
|
||||||
|
class="block text-sm font-medium mb-2"
|
||||||
|
:class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']"
|
||||||
|
>
|
||||||
|
启用代理
|
||||||
|
</label>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="config.s3_proxy = config.s3_proxy === 1 ? 0 : 1"
|
||||||
|
class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||||
|
:class="[config.s3_proxy === 1 ? 'bg-indigo-600' : 'bg-gray-200']"
|
||||||
|
role="switch"
|
||||||
|
:aria-checked="config.s3_proxy === 1"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
|
||||||
|
:class="[
|
||||||
|
config.s3_proxy === 1 ? 'translate-x-5' : 'translate-x-0',
|
||||||
|
isDarkMode && config.s3_proxy !== 1 ? 'bg-gray-100' : 'bg-white'
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="ml-3 text-sm"
|
||||||
|
:class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']"
|
||||||
|
>
|
||||||
|
{{ config.s3_proxy === 1 ? '已开启' : '已关闭' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -716,37 +811,4 @@ refreshData()
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
.custom-scrollbar {
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #cbd5e0;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #a0aec0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 适配暗黑模式 */
|
|
||||||
:deep(.dark &::-webkit-scrollbar-thumb) {
|
|
||||||
background-color: #4a5568;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #2d3748;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保内容区域不会被截断 */
|
|
||||||
.space-y-6 {
|
|
||||||
margin-bottom: 5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user