fix:some bug
This commit is contained in:
@@ -100,14 +100,6 @@ provide('isLoading', isLoading)
|
||||
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-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<script setup>
|
||||
import { FileIcon, CalendarIcon, ClockIcon, KeyIcon, XIcon } from 'lucide-vue-next'
|
||||
import QRCode from 'qrcode.vue'
|
||||
import { useAlertStore } from '@/stores/alertStore'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
|
||||
const props = defineProps({
|
||||
record: Object,
|
||||
@@ -106,17 +106,10 @@ const props = defineProps({
|
||||
baseUrl: String
|
||||
})
|
||||
|
||||
const alertStore = useAlertStore()
|
||||
|
||||
defineEmits(['close', 'show-preview'])
|
||||
|
||||
const copyShareLink = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(`${props.baseUrl}/retrieve/${props.record.code}`)
|
||||
alertStore.showAlert('链接已复制到剪贴板', 'success')
|
||||
} catch (err) {
|
||||
alertStore.showAlert('复制失败,请手动复制', 'error')
|
||||
}
|
||||
copyToClipboard(`${props.baseUrl}/retrieve/${props.record.code}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -236,4 +236,37 @@ input:checked + .slider {
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
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>
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
import axios from 'axios'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
// 从环境变量中获取 API 基础 URL
|
||||
const baseURL =
|
||||
@@ -13,7 +12,7 @@ const sanitizedBaseURL = typeof baseURL === 'string' ? baseURL : ''
|
||||
// 创建 axios 实例
|
||||
const api = axios.create({
|
||||
baseURL: sanitizedBaseURL,
|
||||
timeout: 10000, // 请求超时时间
|
||||
timeout: 1000000000000000, // 请求超时时间
|
||||
headers: {
|
||||
'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>
|
||||
</p>
|
||||
<p :class="['mt-2 text-xs', isDarkMode ? 'text-gray-500' : 'text-gray-400']">
|
||||
支持各种常见格式,最大20MB
|
||||
支持各种常见格式,最大{{ getStorageUnit(config.uploadSize) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -128,11 +128,9 @@
|
||||
: 'bg-white text-gray-900 border border-gray-300'
|
||||
]"
|
||||
>
|
||||
<option value="day">按天数</option>
|
||||
<option value="hour">按小时</option>
|
||||
<option value="minute">按分钟</option>
|
||||
<option value="count">按查看次数</option>
|
||||
<option value="forever">永久</option>
|
||||
<option v-for="item in config.expireStyle" :value="item" :key="item">
|
||||
{{ getUnit(item) }}
|
||||
</option>
|
||||
</select>
|
||||
<div v-if="expirationMethod !== 'forever'" class="flex items-center space-x-2">
|
||||
<div class="relative flex-grow">
|
||||
@@ -414,6 +412,11 @@ import QRCode from 'qrcode.vue'
|
||||
import SparkMD5 from 'spark-md5'
|
||||
import { useFileDataStore } from '../stores/fileData'
|
||||
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 isDarkMode = inject('isDarkMode')
|
||||
@@ -549,8 +552,8 @@ const mergeChunks = async (fileHash: string, totalChunks: number) => {
|
||||
console.log(`请求合并文件切片, fileHash: ${fileHash}, totalChunks: ${totalChunks}`)
|
||||
}
|
||||
|
||||
const getPlaceholder = () => {
|
||||
switch (expirationMethod.value) {
|
||||
const getPlaceholder = (value: string = expirationMethod.value) => {
|
||||
switch (value) {
|
||||
case 'day':
|
||||
return '输入天数'
|
||||
case 'hour':
|
||||
@@ -566,8 +569,8 @@ const getPlaceholder = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const getUnit = () => {
|
||||
switch (expirationMethod.value) {
|
||||
const getUnit = (value: string = expirationMethod.value) => {
|
||||
switch (value) {
|
||||
case 'day':
|
||||
return '天'
|
||||
case 'hour':
|
||||
@@ -703,27 +706,6 @@ const getQRCodeValue = (record: any) => {
|
||||
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(() => {
|
||||
// 这里可以放置一些非立即需要的初始化代码
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<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>
|
||||
|
||||
@@ -33,6 +33,7 @@ interface ConfigState {
|
||||
uploadCount: number
|
||||
errorMinute: number
|
||||
errorCount: number
|
||||
s3_proxy: number
|
||||
}
|
||||
|
||||
const config = ref<ConfigState>({
|
||||
@@ -63,7 +64,8 @@ const config = ref<ConfigState>({
|
||||
s3_hostname: '',
|
||||
uploadCount: 1,
|
||||
errorMinute: 1,
|
||||
errorCount: 1
|
||||
errorCount: 1,
|
||||
s3_proxy: 0
|
||||
})
|
||||
|
||||
const fileSize = ref(1)
|
||||
@@ -439,6 +441,99 @@ refreshData()
|
||||
]"
|
||||
/>
|
||||
</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>
|
||||
@@ -716,37 +811,4 @@ refreshData()
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.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 scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user